summaryrefslogtreecommitdiff
path: root/src/third_party/fmt
diff options
context:
space:
mode:
authorHenrik Edin <henrik.edin@mongodb.com>2019-12-18 19:03:39 +0000
committerevergreen <evergreen@mongodb.com>2019-12-18 19:03:39 +0000
commiteacfb9c57cb58c4be8c0af5051acfafe505387ae (patch)
tree21e49507f6d83f85c50039ea0e22e166312db4e8 /src/third_party/fmt
parentfa86eab8cfeccf6114ea58448f6357a68428c1f7 (diff)
downloadmongo-eacfb9c57cb58c4be8c0af5051acfafe505387ae.tar.gz
SERVER-43743 Upgrade libfmt to 6.1.1
Diffstat (limited to 'src/third_party/fmt')
-rw-r--r--src/third_party/fmt/SConscript5
-rw-r--r--src/third_party/fmt/dist/LICENSE.rst40
-rw-r--r--src/third_party/fmt/dist/include/fmt/chrono.h444
-rw-r--r--src/third_party/fmt/dist/include/fmt/color.h156
-rw-r--r--src/third_party/fmt/dist/include/fmt/compile.h587
-rw-r--r--src/third_party/fmt/dist/include/fmt/core.h352
-rw-r--r--src/third_party/fmt/dist/include/fmt/format-inl.h922
-rw-r--r--src/third_party/fmt/dist/include/fmt/format.h2767
-rw-r--r--src/third_party/fmt/dist/include/fmt/locale.h14
-rw-r--r--src/third_party/fmt/dist/include/fmt/ostream.h45
-rw-r--r--src/third_party/fmt/dist/include/fmt/posix.h56
-rw-r--r--src/third_party/fmt/dist/include/fmt/printf.h228
-rw-r--r--src/third_party/fmt/dist/include/fmt/ranges.h88
-rw-r--r--src/third_party/fmt/dist/src/format.cc30
-rw-r--r--src/third_party/fmt/dist/src/posix.cc14
-rwxr-xr-xsrc/third_party/fmt/scripts/import.sh2
16 files changed, 3629 insertions, 2121 deletions
diff --git a/src/third_party/fmt/SConscript b/src/third_party/fmt/SConscript
index 9b73f97e282..fa31ba7eb55 100644
--- a/src/third_party/fmt/SConscript
+++ b/src/third_party/fmt/SConscript
@@ -3,6 +3,11 @@ Import("env")
Import("has_option")
Import("debugBuild")
env = env.Clone()
+
+# In libfmt 6.1.1, when compiling format.cc, this is needed for function fmt::v6::internal::report_error()
+if env.ToolchainIs('GCC'):
+ env.AppendUnique(CXXFLAGS=['-Wno-error=unused-result'])
+
env.Library(
target='fmt',
source=env.File([
diff --git a/src/third_party/fmt/dist/LICENSE.rst b/src/third_party/fmt/dist/LICENSE.rst
index 6061de720b5..f0ec3db4d2a 100644
--- a/src/third_party/fmt/dist/LICENSE.rst
+++ b/src/third_party/fmt/dist/LICENSE.rst
@@ -1,23 +1,27 @@
Copyright (c) 2012 - present, Victor Zverovich
-All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--- Optional exception to the license ---
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into a machine-executable object form of such
+source code, you may redistribute such embedded portions in such object form
+without including the above copyright and permission notices.
diff --git a/src/third_party/fmt/dist/include/fmt/chrono.h b/src/third_party/fmt/dist/include/fmt/chrono.h
index f47ff75345a..9abe7c4ff39 100644
--- a/src/third_party/fmt/dist/include/fmt/chrono.h
+++ b/src/third_party/fmt/dist/include/fmt/chrono.h
@@ -18,6 +18,290 @@
FMT_BEGIN_NAMESPACE
+// Enable safe chrono durations, unless explicitly disabled.
+#ifndef FMT_SAFE_DURATION_CAST
+# define FMT_SAFE_DURATION_CAST 1
+#endif
+#if FMT_SAFE_DURATION_CAST
+
+// For conversion between std::chrono::durations without undefined
+// behaviour or erroneous results.
+// This is a stripped down version of duration_cast, for inclusion in fmt.
+// See https://github.com/pauldreik/safe_duration_cast
+//
+// Copyright Paul Dreik 2019
+namespace safe_duration_cast {
+
+template <typename To, typename From,
+ FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+ std::numeric_limits<From>::is_signed ==
+ std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+ ec = 0;
+ using F = std::numeric_limits<From>;
+ using T = std::numeric_limits<To>;
+ static_assert(F::is_integer, "From must be integral");
+ static_assert(T::is_integer, "To must be integral");
+
+ // A and B are both signed, or both unsigned.
+ if (F::digits <= T::digits) {
+ // From fits in To without any problem.
+ } else {
+ // From does not always fit in To, resort to a dynamic check.
+ if (from < T::min() || from > T::max()) {
+ // outside range.
+ ec = 1;
+ return {};
+ }
+ }
+ return static_cast<To>(from);
+}
+
+/**
+ * converts From to To, without loss. If the dynamic value of from
+ * can't be converted to To without loss, ec is set.
+ */
+template <typename To, typename From,
+ FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+ std::numeric_limits<From>::is_signed !=
+ std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+ ec = 0;
+ using F = std::numeric_limits<From>;
+ using T = std::numeric_limits<To>;
+ static_assert(F::is_integer, "From must be integral");
+ static_assert(T::is_integer, "To must be integral");
+
+ if (F::is_signed && !T::is_signed) {
+ // From may be negative, not allowed!
+ if (fmt::internal::is_negative(from)) {
+ ec = 1;
+ return {};
+ }
+
+ // From is positive. Can it always fit in To?
+ if (F::digits <= T::digits) {
+ // yes, From always fits in To.
+ } else {
+ // from may not fit in To, we have to do a dynamic check
+ if (from > static_cast<From>(T::max())) {
+ ec = 1;
+ return {};
+ }
+ }
+ }
+
+ if (!F::is_signed && T::is_signed) {
+ // can from be held in To?
+ if (F::digits < T::digits) {
+ // yes, From always fits in To.
+ } else {
+ // from may not fit in To, we have to do a dynamic check
+ if (from > static_cast<From>(T::max())) {
+ // outside range.
+ ec = 1;
+ return {};
+ }
+ }
+ }
+
+ // reaching here means all is ok for lossless conversion.
+ return static_cast<To>(from);
+
+} // function
+
+template <typename To, typename From,
+ FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+ ec = 0;
+ return from;
+} // function
+
+// clang-format off
+/**
+ * converts From to To if possible, otherwise ec is set.
+ *
+ * input | output
+ * ---------------------------------|---------------
+ * NaN | NaN
+ * Inf | Inf
+ * normal, fits in output | converted (possibly lossy)
+ * normal, does not fit in output | ec is set
+ * subnormal | best effort
+ * -Inf | -Inf
+ */
+// clang-format on
+template <typename To, typename From,
+ FMT_ENABLE_IF(!std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+ ec = 0;
+ using T = std::numeric_limits<To>;
+ static_assert(std::is_floating_point<From>::value, "From must be floating");
+ static_assert(std::is_floating_point<To>::value, "To must be floating");
+
+ // catch the only happy case
+ if (std::isfinite(from)) {
+ if (from >= T::lowest() && from <= T::max()) {
+ return static_cast<To>(from);
+ }
+ // not within range.
+ ec = 1;
+ return {};
+ }
+
+ // nan and inf will be preserved
+ return static_cast<To>(from);
+} // function
+
+template <typename To, typename From,
+ FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+ ec = 0;
+ static_assert(std::is_floating_point<From>::value, "From must be floating");
+ return from;
+}
+
+/**
+ * safe duration cast between integral durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(std::is_integral<FromRep>::value),
+ FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+ int& ec) {
+ using From = std::chrono::duration<FromRep, FromPeriod>;
+ ec = 0;
+ // the basic idea is that we need to convert from count() in the from type
+ // to count() in the To type, by multiplying it with this:
+ struct Factor
+ : std::ratio_divide<typename From::period, typename To::period> {};
+
+ static_assert(Factor::num > 0, "num must be positive");
+ static_assert(Factor::den > 0, "den must be positive");
+
+ // the conversion is like this: multiply from.count() with Factor::num
+ // /Factor::den and convert it to To::rep, all this without
+ // overflow/underflow. let's start by finding a suitable type that can hold
+ // both To, From and Factor::num
+ using IntermediateRep =
+ typename std::common_type<typename From::rep, typename To::rep,
+ decltype(Factor::num)>::type;
+
+ // safe conversion to IntermediateRep
+ IntermediateRep count =
+ lossless_integral_conversion<IntermediateRep>(from.count(), ec);
+ if (ec) {
+ return {};
+ }
+ // multiply with Factor::num without overflow or underflow
+ if (Factor::num != 1) {
+ const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
+ if (count > max1) {
+ ec = 1;
+ return {};
+ }
+ const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
+ if (count < min1) {
+ ec = 1;
+ return {};
+ }
+ count *= Factor::num;
+ }
+
+ // this can't go wrong, right? den>0 is checked earlier.
+ if (Factor::den != 1) {
+ count /= Factor::den;
+ }
+ // convert to the to type, safely
+ using ToRep = typename To::rep;
+ const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
+ if (ec) {
+ return {};
+ }
+ return To{tocount};
+}
+
+/**
+ * safe duration_cast between floating point durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
+ FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+ int& ec) {
+ using From = std::chrono::duration<FromRep, FromPeriod>;
+ ec = 0;
+ if (std::isnan(from.count())) {
+ // nan in, gives nan out. easy.
+ return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
+ }
+ // maybe we should also check if from is denormal, and decide what to do about
+ // it.
+
+ // +-inf should be preserved.
+ if (std::isinf(from.count())) {
+ return To{from.count()};
+ }
+
+ // the basic idea is that we need to convert from count() in the from type
+ // to count() in the To type, by multiplying it with this:
+ struct Factor
+ : std::ratio_divide<typename From::period, typename To::period> {};
+
+ static_assert(Factor::num > 0, "num must be positive");
+ static_assert(Factor::den > 0, "den must be positive");
+
+ // the conversion is like this: multiply from.count() with Factor::num
+ // /Factor::den and convert it to To::rep, all this without
+ // overflow/underflow. let's start by finding a suitable type that can hold
+ // both To, From and Factor::num
+ using IntermediateRep =
+ typename std::common_type<typename From::rep, typename To::rep,
+ decltype(Factor::num)>::type;
+
+ // force conversion of From::rep -> IntermediateRep to be safe,
+ // even if it will never happen be narrowing in this context.
+ IntermediateRep count =
+ safe_float_conversion<IntermediateRep>(from.count(), ec);
+ if (ec) {
+ return {};
+ }
+
+ // multiply with Factor::num without overflow or underflow
+ if (Factor::num != 1) {
+ constexpr auto max1 = internal::max_value<IntermediateRep>() /
+ static_cast<IntermediateRep>(Factor::num);
+ if (count > max1) {
+ ec = 1;
+ return {};
+ }
+ constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
+ static_cast<IntermediateRep>(Factor::num);
+ if (count < min1) {
+ ec = 1;
+ return {};
+ }
+ count *= static_cast<IntermediateRep>(Factor::num);
+ }
+
+ // this can't go wrong, right? den>0 is checked earlier.
+ if (Factor::den != 1) {
+ using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
+ count /= static_cast<common_t>(Factor::den);
+ }
+
+ // convert to the to type, safely
+ using ToRep = typename To::rep;
+
+ const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
+ if (ec) {
+ return {};
+ }
+ return To{tocount};
+}
+} // namespace safe_duration_cast
+#endif
+
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
@@ -385,7 +669,16 @@ inline bool isnan(T value) {
return std::isnan(value);
}
-// Convers value to int and checks that it's in the range [0, upper).
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline bool isfinite(T) {
+ return true;
+}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+inline bool isfinite(T value) {
+ return std::isfinite(value);
+}
+
+// Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
@@ -407,7 +700,7 @@ inline T mod(T x, int y) {
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
- return std::fmod(x, y);
+ return std::fmod(x, static_cast<T>(y));
}
// If T is an integral type, maps T to its unsigned counterpart, otherwise
@@ -421,19 +714,48 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type;
};
+#if FMT_SAFE_DURATION_CAST
+// throwing version of safe_duration_cast
+template <typename To, typename FromRep, typename FromPeriod>
+To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
+ int ec;
+ To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
+ if (ec) FMT_THROW(format_error("cannot format duration"));
+ return to;
+}
+#endif
+
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
+ // this may overflow and/or the result may not fit in the
+ // target type.
+#if FMT_SAFE_DURATION_CAST
+ using CommonSecondsType =
+ typename std::common_type<decltype(d), std::chrono::seconds>::type;
+ const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
+ const auto d_as_whole_seconds =
+ fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
+ // this conversion should be nonproblematic
+ const auto diff = d_as_common - d_as_whole_seconds;
+ const auto ms =
+ fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+ return ms;
+#else
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
+#endif
}
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
- auto ms = mod(d.count() * Period::num / Period::den * 1000, 1000);
+ using common_type = typename std::common_type<Rep, std::intmax_t>::type;
+ auto ms = mod(d.count() * static_cast<common_type>(Period::num) /
+ static_cast<common_type>(Period::den) * 1000,
+ 1000);
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
}
@@ -462,22 +784,49 @@ struct chrono_formatter {
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
rep val;
- typedef std::chrono::duration<rep> seconds;
+ using seconds = std::chrono::duration<rep>;
seconds s;
- typedef std::chrono::duration<rep, std::milli> milliseconds;
+ using milliseconds = std::chrono::duration<rep, std::milli>;
bool negative;
- typedef typename FormatContext::char_type char_type;
+ using char_type = typename FormatContext::char_type;
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
if (d.count() < 0) {
- val = -val;
+ val = 0 - val;
negative = true;
}
+
+ // this may overflow and/or the result may not fit in the
+ // target type.
+#if FMT_SAFE_DURATION_CAST
+ // might need checked conversion (rep!=Rep)
+ auto tmpval = std::chrono::duration<rep, Period>(val);
+ s = fmt_safe_duration_cast<seconds>(tmpval);
+#else
s = std::chrono::duration_cast<seconds>(
std::chrono::duration<rep, Period>(val));
+#endif
+ }
+
+ // returns true if nan or inf, writes to out.
+ bool handle_nan_inf() {
+ if (isfinite(val)) {
+ return false;
+ }
+ if (isnan(val)) {
+ write_nan();
+ return true;
+ }
+ // must be +-inf
+ if (val > 0) {
+ write_pinf();
+ } else {
+ write_ninf();
+ }
+ return true;
}
Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
@@ -508,15 +857,16 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
- typedef typename int_traits<int>::main_type main_type;
- main_type n = to_unsigned(
- to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
+ uint32_or_64_or_128_t<int> n =
+ to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
}
void write_nan() { std::copy_n("nan", 3, out); }
+ void write_pinf() { std::copy_n("inf", 3, out); }
+ void write_ninf() { std::copy_n("-inf", 4, out); }
void format_localized(const tm& time, const char* format) {
if (isnan(val)) return write_nan();
@@ -549,6 +899,8 @@ struct chrono_formatter {
void on_tz_name() {}
void on_24_hour(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
@@ -556,6 +908,8 @@ struct chrono_formatter {
}
void on_12_hour(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
@@ -563,6 +917,8 @@ struct chrono_formatter {
}
void on_minute(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
@@ -570,9 +926,19 @@ struct chrono_formatter {
}
void on_second(numeric_system ns) {
+ if (handle_nan_inf()) return;
+
if (ns == numeric_system::standard) {
write(second(), 2);
- auto ms = get_milliseconds(std::chrono::duration<Rep, Period>(val));
+#if FMT_SAFE_DURATION_CAST
+ // convert rep->Rep
+ using duration_rep = std::chrono::duration<rep, Period>;
+ using duration_Rep = std::chrono::duration<Rep, Period>;
+ auto tmpval = fmt_safe_duration_cast<duration_Rep>(duration_rep{val});
+#else
+ auto tmpval = std::chrono::duration<Rep, Period>(val);
+#endif
+ auto ms = get_milliseconds(tmpval);
if (ms != std::chrono::milliseconds(0)) {
*out++ = '.';
write(ms.count(), 3);
@@ -584,9 +950,19 @@ struct chrono_formatter {
format_localized(time, "%OS");
}
- void on_12_hour_time() { format_localized(time(), "%r"); }
+ void on_12_hour_time() {
+ if (handle_nan_inf()) return;
+
+ format_localized(time(), "%r");
+ }
void on_24_hour_time() {
+ if (handle_nan_inf()) {
+ *out++ = ':';
+ handle_nan_inf();
+ return;
+ }
+
write(hour(), 2);
*out++ = ':';
write(minute(), 2);
@@ -595,12 +971,17 @@ struct chrono_formatter {
void on_iso_time() {
on_24_hour_time();
*out++ = ':';
+ if (handle_nan_inf()) return;
write(second(), 2);
}
- void on_am_pm() { format_localized(time(), "%p"); }
+ void on_am_pm() {
+ if (handle_nan_inf()) return;
+ format_localized(time(), "%p");
+ }
void on_duration_value() {
+ if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
}
@@ -612,17 +993,17 @@ struct chrono_formatter {
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
- align_spec spec;
+ basic_format_specs<Char> specs;
int precision;
- typedef internal::arg_ref<Char> arg_ref_type;
+ using arg_ref_type = internal::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
- typedef std::chrono::duration<Rep, Period> duration;
+ using duration = std::chrono::duration<Rep, Period>;
struct spec_handler {
formatter& f;
- basic_parse_context<Char>& context;
+ basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@@ -632,8 +1013,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
- const auto str_val = internal::string_view_metadata(format_str, arg_id);
- return arg_ref_type(str_val);
+ return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
@@ -641,10 +1021,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
- void on_fill(Char fill) { f.spec.fill_ = fill; }
- void on_align(alignment align) { f.spec.align_ = align; }
- void on_width(unsigned width) { f.spec.width_ = width; }
- void on_precision(unsigned precision) { f.precision = precision; }
+ void on_fill(Char fill) { f.specs.fill[0] = fill; }
+ void on_align(align_t align) { f.specs.align = align; }
+ void on_width(unsigned width) { f.specs.width = width; }
+ void on_precision(unsigned _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
@@ -656,13 +1036,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
- typedef typename basic_parse_context<Char>::iterator iterator;
+ using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
- FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
+ FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
@@ -681,9 +1061,9 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
public:
- formatter() : spec(), precision(-1) {}
+ formatter() : precision(-1) {}
- FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
+ FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
@@ -699,11 +1079,11 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
- basic_writer<range> w(range(ctx.out()));
- internal::handle_dynamic_spec<internal::width_checker>(
- spec.width_, width_ref, ctx, format_str.begin());
+ internal::basic_writer<range> w(range(ctx.out()));
+ internal::handle_dynamic_spec<internal::width_checker>(specs.width,
+ width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
- precision, precision_ref, ctx, format_str.begin());
+ precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
@@ -713,7 +1093,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
f.precision = precision;
parse_chrono_format(begin, end, f);
}
- w.write(buf.data(), buf.size(), spec);
+ w.write(buf.data(), buf.size(), specs);
return w.out();
}
};
diff --git a/src/third_party/fmt/dist/include/fmt/color.h b/src/third_party/fmt/dist/include/fmt/color.h
index f1683a7181f..362a95e142e 100644
--- a/src/third_party/fmt/dist/include/fmt/color.h
+++ b/src/third_party/fmt/dist/include/fmt/color.h
@@ -12,54 +12,6 @@
FMT_BEGIN_NAMESPACE
-#ifdef FMT_DEPRECATED_COLORS
-
-// color and (v)print_colored are deprecated.
-enum FMT_DEPRECATED color {
- black,
- red,
- green,
- yellow,
- blue,
- magenta,
- cyan,
- white
-};
-FMT_DEPRECATED FMT_API void vprint_colored(color c, string_view format,
- format_args args);
-FMT_DEPRECATED FMT_API void vprint_colored(color c, wstring_view format,
- wformat_args args);
-template <typename... Args>
-FMT_DEPRECATED inline void print_colored(color c, string_view format_str,
- const Args&... args) {
- vprint_colored(c, format_str, make_format_args(args...));
-}
-template <typename... Args>
-FMT_DEPRECATED inline void print_colored(color c, wstring_view format_str,
- const Args&... args) {
- vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
-}
-
-FMT_DEPRECATED inline void vprint_colored(color c, string_view format,
- format_args args) {
- char escape[] = "\x1b[30m";
- escape[3] = static_cast<char>('0' + c);
- std::fputs(escape, stdout);
- vprint(format, args);
- std::fputs(internal::data::RESET_COLOR, stdout);
-}
-
-FMT_DEPRECATED inline void vprint_colored(color c, wstring_view format,
- wformat_args args) {
- wchar_t escape[] = L"\x1b[30m";
- escape[3] = static_cast<wchar_t>('0' + c);
- std::fputws(escape, stdout);
- vprint(format, args);
- std::fputws(internal::data::WRESET_COLOR, stdout);
-}
-
-#else
-
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
@@ -221,18 +173,17 @@ enum class terminal_color : uint8_t {
bright_magenta,
bright_cyan,
bright_white
-}; // enum class terminal_color
+};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
-}; // enum class emphasis
+};
// rgb is a struct for red, green and blue colors.
-// We use rgb as name because some editors will show it as color direct in the
-// editor.
+// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
@@ -286,7 +237,7 @@ class text_style {
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
- throw format_error("can't OR a terminal color");
+ FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@@ -295,7 +246,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
- throw format_error("can't OR a terminal color");
+ FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@@ -315,7 +266,7 @@ class text_style {
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
- throw format_error("can't AND a terminal color");
+ FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
@@ -324,7 +275,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
- throw format_error("can't AND a terminal color");
+ FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
@@ -348,15 +299,15 @@ class text_style {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
- assert(has_foreground() && "no foreground specified for this style");
+ FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
- assert(has_background() && "no background specified for this style");
+ FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
- assert(has_emphasis() && "no emphasis specified for this style");
+ FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@@ -407,7 +358,7 @@ template <typename Char> struct ansi_color_escape {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
- bool is_background = esc == internal::data::BACKGROUND_COLOR;
+ bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
@@ -479,13 +430,13 @@ template <typename Char> struct ansi_color_escape {
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
- return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
+ return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
- return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
+ return ansi_color_escape<Char>(background, internal::data::background_color);
}
template <typename Char>
@@ -504,73 +455,56 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
- fputs(internal::data::RESET_COLOR, stream);
+ fputs(internal::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
- fputs(internal::data::WRESET_COLOR, stream);
+ fputs(internal::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
- const char* begin = data::RESET_COLOR;
- const char* end = begin + sizeof(data::RESET_COLOR) - 1;
+ const char* begin = data::reset_color;
+ const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
-std::basic_string<Char> vformat(const text_style& ts,
- basic_string_view<Char> format_str,
- basic_format_args<buffer_context<Char> > args) {
- basic_memory_buffer<Char> buffer;
+void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
+ basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
- ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
- buffer.append(escape.begin(), escape.end());
+ auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
+ buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
- ansi_color_escape<Char> escape =
- make_foreground_color<Char>(ts.get_foreground());
- buffer.append(escape.begin(), escape.end());
+ auto foreground =
+ internal::make_foreground_color<Char>(ts.get_foreground());
+ buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
- ansi_color_escape<Char> escape =
- make_background_color<Char>(ts.get_background());
- buffer.append(escape.begin(), escape.end());
+ auto background =
+ internal::make_background_color<Char>(ts.get_background());
+ buf.append(background.begin(), background.end());
}
- internal::vformat_to(buffer, format_str, args);
+ vformat_to(buf, format_str, args);
if (has_style) {
- reset_color<Char>(buffer);
+ internal::reset_color<Char>(buf);
}
- return fmt::to_string(buffer);
}
} // namespace internal
-template <typename S, typename Char = char_t<S> >
+template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
- basic_format_args<buffer_context<Char> > args) {
- bool has_style = false;
- if (ts.has_emphasis()) {
- has_style = true;
- internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
- }
- if (ts.has_foreground()) {
- has_style = true;
- internal::fputs<Char>(
- internal::make_foreground_color<Char>(ts.get_foreground()), f);
- }
- if (ts.has_background()) {
- has_style = true;
- internal::fputs<Char>(
- internal::make_background_color<Char>(ts.get_background()), f);
- }
- vprint(f, format, args);
- if (has_style) {
- internal::reset_color<Char>(f);
- }
+ basic_format_args<buffer_context<Char>> args) {
+ basic_memory_buffer<Char> buf;
+ internal::vformat_to(buf, ts, to_string_view(format), args);
+ buf.push_back(Char(0));
+ internal::fputs(buf.data(), f);
}
/**
@@ -585,7 +519,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
- using context = buffer_context<char_t<S> >;
+ using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
@@ -603,11 +537,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
-template <typename S, typename Char = char_t<S> >
+template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
- basic_format_args<buffer_context<Char> > args) {
- return internal::vformat(ts, to_string_view(format_str), args);
+ basic_format_args<buffer_context<Char>> args) {
+ basic_memory_buffer<Char> buf;
+ internal::vformat_to(buf, ts, to_string_view(format_str), args);
+ return fmt::to_string(buf);
}
/**
@@ -622,15 +558,13 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
-template <typename S, typename... Args, typename Char = char_t<S> >
+template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
- return internal::vformat(ts, to_string_view(format_str),
- {internal::make_args_checked(format_str, args...)});
+ return vformat(ts, to_string_view(format_str),
+ {internal::make_args_checked<Args...>(format_str, args...)});
}
-#endif
-
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_
diff --git a/src/third_party/fmt/dist/include/fmt/compile.h b/src/third_party/fmt/dist/include/fmt/compile.h
new file mode 100644
index 00000000000..f65f5a74dc2
--- /dev/null
+++ b/src/third_party/fmt/dist/include/fmt/compile.h
@@ -0,0 +1,587 @@
+// Formatting library for C++ - experimental format string compilation
+//
+// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COMPILE_H_
+#define FMT_COMPILE_H_
+
+#include <vector>
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace internal {
+
+// Part of a compiled format string. It can be either literal text or a
+// replacement field.
+template <typename Char> struct format_part {
+ enum class kind { arg_index, arg_name, text, replacement };
+
+ struct replacement {
+ arg_ref<Char> arg_id;
+ dynamic_format_specs<Char> specs;
+ };
+
+ kind part_kind;
+ union value {
+ unsigned arg_index;
+ basic_string_view<Char> str;
+ replacement repl;
+
+ FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
+ FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
+ FMT_CONSTEXPR value(replacement r) : repl(r) {}
+ } val;
+ // Position past the end of the argument id.
+ const Char* arg_id_end = nullptr;
+
+ FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
+ : part_kind(k), val(v) {}
+
+ static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
+ return format_part(kind::arg_index, index);
+ }
+ static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
+ return format_part(kind::arg_name, name);
+ }
+ static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
+ return format_part(kind::text, text);
+ }
+ static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
+ return format_part(kind::replacement, repl);
+ }
+};
+
+template <typename Char> struct part_counter {
+ unsigned num_parts = 0;
+
+ FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
+ if (begin != end) ++num_parts;
+ }
+
+ FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
+ FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
+ FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
+
+ FMT_CONSTEXPR void on_replacement_field(const Char*) {}
+
+ FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
+ const Char* end) {
+ // Find the matching brace.
+ unsigned brace_counter = 0;
+ for (; begin != end; ++begin) {
+ if (*begin == '{') {
+ ++brace_counter;
+ } else if (*begin == '}') {
+ if (brace_counter == 0u) break;
+ --brace_counter;
+ }
+ }
+ return begin;
+ }
+
+ FMT_CONSTEXPR void on_error(const char*) {}
+};
+
+// Counts the number of parts in a format string.
+template <typename Char>
+FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
+ part_counter<Char> counter;
+ parse_format_string<true>(format_str, counter);
+ return counter.num_parts;
+}
+
+template <typename Char, typename PartHandler>
+class format_string_compiler : public error_handler {
+ private:
+ using part = format_part<Char>;
+
+ PartHandler handler_;
+ part part_;
+ basic_string_view<Char> format_str_;
+ basic_format_parse_context<Char> parse_context_;
+
+ public:
+ FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
+ PartHandler handler)
+ : handler_(handler),
+ format_str_(format_str),
+ parse_context_(format_str) {}
+
+ FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
+ if (begin != end)
+ handler_(part::make_text({begin, to_unsigned(end - begin)}));
+ }
+
+ FMT_CONSTEXPR void on_arg_id() {
+ part_ = part::make_arg_index(parse_context_.next_arg_id());
+ }
+
+ FMT_CONSTEXPR void on_arg_id(unsigned id) {
+ parse_context_.check_arg_id(id);
+ part_ = part::make_arg_index(id);
+ }
+
+ FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
+ part_ = part::make_arg_name(id);
+ }
+
+ FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
+ part_.arg_id_end = ptr;
+ handler_(part_);
+ }
+
+ FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
+ const Char* end) {
+ auto repl = typename part::replacement();
+ dynamic_specs_handler<basic_format_parse_context<Char>> handler(
+ repl.specs, parse_context_);
+ auto it = parse_format_specs(begin, end, handler);
+ if (*it != '}') on_error("missing '}' in format string");
+ repl.arg_id = part_.part_kind == part::kind::arg_index
+ ? arg_ref<Char>(part_.val.arg_index)
+ : arg_ref<Char>(part_.val.str);
+ auto part = part::make_replacement(repl);
+ part.arg_id_end = begin;
+ handler_(part);
+ return it;
+ }
+};
+
+// Compiles a format string and invokes handler(part) for each parsed part.
+template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
+FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
+ PartHandler handler) {
+ parse_format_string<IS_CONSTEXPR>(
+ format_str,
+ format_string_compiler<Char, PartHandler>(format_str, handler));
+}
+
+template <typename Range, typename Context, typename Id>
+void format_arg(
+ basic_format_parse_context<typename Range::value_type>& parse_ctx,
+ Context& ctx, Id arg_id) {
+ ctx.advance_to(
+ visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
+}
+
+// vformat_to is defined in a subnamespace to prevent ADL.
+namespace cf {
+template <typename Context, typename Range, typename CompiledFormat>
+auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
+ -> typename Context::iterator {
+ using char_type = typename Context::char_type;
+ basic_format_parse_context<char_type> parse_ctx(
+ to_string_view(cf.format_str_));
+ Context ctx(out.begin(), args);
+
+ const auto& parts = cf.parts();
+ for (auto part_it = std::begin(parts); part_it != std::end(parts);
+ ++part_it) {
+ const auto& part = *part_it;
+ const auto& value = part.val;
+
+ using format_part_t = format_part<char_type>;
+ switch (part.part_kind) {
+ case format_part_t::kind::text: {
+ const auto text = value.str;
+ auto output = ctx.out();
+ auto&& it = reserve(output, text.size());
+ it = std::copy_n(text.begin(), text.size(), it);
+ ctx.advance_to(output);
+ break;
+ }
+
+ case format_part_t::kind::arg_index:
+ advance_to(parse_ctx, part.arg_id_end);
+ internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
+ break;
+
+ case format_part_t::kind::arg_name:
+ advance_to(parse_ctx, part.arg_id_end);
+ internal::format_arg<Range>(parse_ctx, ctx, value.str);
+ break;
+
+ case format_part_t::kind::replacement: {
+ const auto& arg_id_value = value.repl.arg_id.val;
+ const auto arg = value.repl.arg_id.kind == arg_id_kind::index
+ ? ctx.arg(arg_id_value.index)
+ : ctx.arg(arg_id_value.name);
+
+ auto specs = value.repl.specs;
+
+ handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
+ handle_dynamic_spec<precision_checker>(specs.precision,
+ specs.precision_ref, ctx);
+
+ error_handler h;
+ numeric_specs_checker<error_handler> checker(h, arg.type());
+ if (specs.align == align::numeric) checker.require_numeric_argument();
+ if (specs.sign != sign::none) checker.check_sign();
+ if (specs.alt) checker.require_numeric_argument();
+ if (specs.precision >= 0) checker.check_precision();
+
+ advance_to(parse_ctx, part.arg_id_end);
+ ctx.advance_to(
+ visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
+ break;
+ }
+ }
+ }
+ return ctx.out();
+}
+} // namespace cf
+
+struct basic_compiled_format {};
+
+template <typename S, typename = void>
+struct compiled_format_base : basic_compiled_format {
+ using char_type = char_t<S>;
+ using parts_container = std::vector<internal::format_part<char_type>>;
+
+ parts_container compiled_parts;
+
+ explicit compiled_format_base(basic_string_view<char_type> format_str) {
+ compile_format_string<false>(format_str,
+ [this](const format_part<char_type>& part) {
+ compiled_parts.push_back(part);
+ });
+ }
+
+ const parts_container& parts() const { return compiled_parts; }
+};
+
+template <typename Char, unsigned N> struct format_part_array {
+ format_part<Char> data[N] = {};
+ FMT_CONSTEXPR format_part_array() = default;
+};
+
+template <typename Char, unsigned N>
+FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
+ basic_string_view<Char> format_str) {
+ format_part_array<Char, N> parts;
+ unsigned counter = 0;
+ // This is not a lambda for compatibility with older compilers.
+ struct {
+ format_part<Char>* parts;
+ unsigned* counter;
+ FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
+ parts[(*counter)++] = part;
+ }
+ } collector{parts.data, &counter};
+ compile_format_string<true>(format_str, collector);
+ if (counter < N) {
+ parts.data[counter] =
+ format_part<Char>::make_text(basic_string_view<Char>());
+ }
+ return parts;
+}
+
+template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
+ return (a < b) ? b : a;
+}
+
+template <typename S>
+struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
+ : basic_compiled_format {
+ using char_type = char_t<S>;
+
+ FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
+
+// Workaround for old compilers. Format string compilation will not be
+// performed there anyway.
+#if FMT_USE_CONSTEXPR
+ static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
+ constexpr_max(count_parts(to_string_view(S())), 1u);
+#else
+ static const unsigned num_format_parts = 1;
+#endif
+
+ using parts_container = format_part<char_type>[num_format_parts];
+
+ const parts_container& parts() const {
+ static FMT_CONSTEXPR_DECL const auto compiled_parts =
+ compile_to_parts<char_type, num_format_parts>(
+ internal::to_string_view(S()));
+ return compiled_parts.data;
+ }
+};
+
+template <typename S, typename... Args>
+class compiled_format : private compiled_format_base<S> {
+ public:
+ using typename compiled_format_base<S>::char_type;
+
+ private:
+ basic_string_view<char_type> format_str_;
+
+ template <typename Context, typename Range, typename CompiledFormat>
+ friend auto cf::vformat_to(Range out, CompiledFormat& cf,
+ basic_format_args<Context> args) ->
+ typename Context::iterator;
+
+ public:
+ compiled_format() = delete;
+ explicit constexpr compiled_format(basic_string_view<char_type> format_str)
+ : compiled_format_base<S>(format_str), format_str_(format_str) {}
+};
+
+#ifdef __cpp_if_constexpr
+template <typename... Args> struct type_list {};
+
+// Returns a reference to the argument at index N from [first, rest...].
+template <int N, typename T, typename... Args>
+constexpr const auto& get(const T& first, const Args&... rest) {
+ static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
+ if constexpr (N == 0)
+ return first;
+ else
+ return get<N - 1>(rest...);
+}
+
+template <int N, typename> struct get_type_impl;
+
+template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
+ using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
+};
+
+template <int N, typename T>
+using get_type = typename get_type_impl<N, T>::type;
+
+template <typename Char> struct text {
+ basic_string_view<Char> data;
+ using char_type = Char;
+
+ template <typename OutputIt, typename... Args>
+ OutputIt format(OutputIt out, const Args&...) const {
+ // TODO: reserve
+ return copy_str<Char>(data.begin(), data.end(), out);
+ }
+};
+
+template <typename Char>
+constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
+ size_t size) {
+ return {{&s[pos], size}};
+}
+
+template <typename Char, typename OutputIt, typename T,
+ std::enable_if_t<std::is_integral_v<T>, int> = 0>
+OutputIt format_default(OutputIt out, T value) {
+ // TODO: reserve
+ format_int fi(value);
+ return std::copy(fi.data(), fi.data() + fi.size(), out);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt format_default(OutputIt out, double value) {
+ writer w(out);
+ w.write(value);
+ return w.out();
+}
+
+template <typename Char, typename OutputIt>
+OutputIt format_default(OutputIt out, Char value) {
+ *out++ = value;
+ return out;
+}
+
+template <typename Char, typename OutputIt>
+OutputIt format_default(OutputIt out, const Char* value) {
+ auto length = std::char_traits<Char>::length(value);
+ return copy_str<Char>(value, value + length, out);
+}
+
+// A replacement field that refers to argument N.
+template <typename Char, typename T, int N> struct field {
+ using char_type = Char;
+
+ template <typename OutputIt, typename... Args>
+ OutputIt format(OutputIt out, const Args&... args) const {
+ // This ensures that the argument type is convertile to `const T&`.
+ const T& arg = get<N>(args...);
+ return format_default<Char>(out, arg);
+ }
+};
+
+template <typename L, typename R> struct concat {
+ L lhs;
+ R rhs;
+ using char_type = typename L::char_type;
+
+ template <typename OutputIt, typename... Args>
+ OutputIt format(OutputIt out, const Args&... args) const {
+ out = lhs.format(out, args...);
+ return rhs.format(out, args...);
+ }
+};
+
+template <typename L, typename R>
+constexpr concat<L, R> make_concat(L lhs, R rhs) {
+ return {lhs, rhs};
+}
+
+struct unknown_format {};
+
+template <typename Char>
+constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
+ for (size_t size = str.size(); pos != size; ++pos) {
+ if (str[pos] == '{' || str[pos] == '}') break;
+ }
+ return pos;
+}
+
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str);
+
+template <typename Args, size_t POS, int ID, typename T, typename S>
+constexpr auto parse_tail(T head, S format_str) {
+ if constexpr (POS != to_string_view(format_str).size()) {
+ constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
+ if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
+ unknown_format>())
+ return tail;
+ else
+ return make_concat(head, tail);
+ } else {
+ return head;
+ }
+}
+
+// Compiles a non-empty format string and returns the compiled representation
+// or unknown_format() on unrecognized input.
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str) {
+ using char_type = typename S::char_type;
+ constexpr basic_string_view<char_type> str = format_str;
+ if constexpr (str[POS] == '{') {
+ if (POS + 1 == str.size())
+ throw format_error("unmatched '{' in format string");
+ if constexpr (str[POS + 1] == '{') {
+ return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+ } else if constexpr (str[POS + 1] == '}') {
+ using type = get_type<ID, Args>;
+ if constexpr (std::is_same<type, int>::value) {
+ return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
+ format_str);
+ } else {
+ return unknown_format();
+ }
+ } else {
+ return unknown_format();
+ }
+ } else if constexpr (str[POS] == '}') {
+ if (POS + 1 == str.size())
+ throw format_error("unmatched '}' in format string");
+ return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+ } else {
+ constexpr auto end = parse_text(str, POS + 1);
+ return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
+ format_str);
+ }
+}
+#endif // __cpp_if_constexpr
+} // namespace internal
+
+#if FMT_USE_CONSTEXPR
+# ifdef __cpp_if_constexpr
+template <typename... Args, typename S,
+ FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr auto compile(S format_str) {
+ constexpr basic_string_view<typename S::char_type> str = format_str;
+ if constexpr (str.size() == 0) {
+ return internal::make_text(str, 0, 0);
+ } else {
+ constexpr auto result =
+ internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
+ format_str);
+ if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
+ internal::unknown_format>()) {
+ return internal::compiled_format<S, Args...>(to_string_view(format_str));
+ } else {
+ return result;
+ }
+ }
+}
+
+template <typename CompiledFormat, typename... Args,
+ typename Char = typename CompiledFormat::char_type,
+ FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
+ CompiledFormat>::value)>
+std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
+ basic_memory_buffer<Char> buffer;
+ using range = buffer_range<Char>;
+ using context = buffer_context<Char>;
+ cf.format(std::back_inserter(buffer), args...);
+ return to_string(buffer);
+}
+
+template <typename OutputIt, typename CompiledFormat, typename... Args,
+ FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
+ CompiledFormat>::value)>
+OutputIt format_to(OutputIt out, const CompiledFormat& cf,
+ const Args&... args) {
+ return cf.format(out, args...);
+}
+# else
+template <typename... Args, typename S,
+ FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
+ return internal::compiled_format<S, Args...>(to_string_view(format_str));
+}
+# endif // __cpp_if_constexpr
+#endif // FMT_USE_CONSTEXPR
+
+// Compiles the format string which must be a string literal.
+template <typename... Args, typename Char, size_t N>
+auto compile(const Char (&format_str)[N])
+ -> internal::compiled_format<const Char*, Args...> {
+ return internal::compiled_format<const Char*, Args...>(
+ basic_string_view<Char>(format_str, N - 1));
+}
+
+template <typename CompiledFormat, typename... Args,
+ typename Char = typename CompiledFormat::char_type,
+ FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
+ CompiledFormat>::value)>
+std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
+ basic_memory_buffer<Char> buffer;
+ using range = buffer_range<Char>;
+ using context = buffer_context<Char>;
+ internal::cf::vformat_to<context>(range(buffer), cf,
+ {make_format_args<context>(args...)});
+ return to_string(buffer);
+}
+
+template <typename OutputIt, typename CompiledFormat, typename... Args,
+ FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
+ CompiledFormat>::value)>
+OutputIt format_to(OutputIt out, const CompiledFormat& cf,
+ const Args&... args) {
+ using char_type = typename CompiledFormat::char_type;
+ using range = internal::output_range<OutputIt, char_type>;
+ using context = format_context_t<OutputIt, char_type>;
+ return internal::cf::vformat_to<context>(
+ range(out), cf, {make_format_args<context>(args...)});
+}
+
+template <typename OutputIt, typename CompiledFormat, typename... Args,
+ FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
+format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
+ const CompiledFormat& cf,
+ const Args&... args) {
+ auto it =
+ format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
+ return {it.base(), it.count()};
+}
+
+template <typename CompiledFormat, typename... Args>
+std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
+ return format_to(internal::counting_iterator(), cf, args...).count();
+}
+
+FMT_END_NAMESPACE
+
+#endif // FMT_COMPILE_H_
diff --git a/src/third_party/fmt/dist/include/fmt/core.h b/src/third_party/fmt/dist/include/fmt/core.h
index 52c812bedcc..13d74ba8372 100644
--- a/src/third_party/fmt/dist/include/fmt/core.h
+++ b/src/third_party/fmt/dist/include/fmt/core.h
@@ -8,7 +8,6 @@
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
-#include <cassert>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
@@ -16,7 +15,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 50301
+#define FMT_VERSION 60101
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@@ -49,6 +48,12 @@
# define FMT_HAS_GXX_CXX11 0
#endif
+#ifdef __NVCC__
+# define FMT_NVCC __NVCC__
+#else
+# define FMT_NVCC 0
+#endif
+
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
#else
@@ -60,7 +65,8 @@
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
- (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
+ (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
+ !FMT_NVCC
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
@@ -133,6 +139,13 @@
# endif
#endif
+// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
+#if defined(__INTEL_COMPILER) || FMT_NVCC
+# define FMT_DEPRECATED_ALIAS
+#else
+# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
+#endif
+
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
@@ -144,12 +157,12 @@
# define FMT_INLINE_NAMESPACE namespace
# define FMT_END_NAMESPACE \
} \
- using namespace v5; \
+ using namespace v6; \
}
# endif
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
- FMT_INLINE_NAMESPACE v5 {
+ FMT_INLINE_NAMESPACE v6 {
#endif
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
@@ -173,10 +186,6 @@
# define FMT_EXTERN
#endif
-#ifndef FMT_ASSERT
-# define FMT_ASSERT(condition, message) assert((condition) && message)
-#endif
-
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@@ -198,6 +207,10 @@ using conditional_t = typename std::conditional<B, T, F>::type;
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
struct monostate {};
@@ -208,6 +221,22 @@ struct monostate {};
namespace internal {
+// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
+template <typename... Ts> struct void_t_impl { using type = void; };
+
+FMT_API void assert_fail(const char* file, int line, const char* message);
+
+#ifndef FMT_ASSERT
+# ifdef NDEBUG
+# define FMT_ASSERT(condition, message)
+# else
+# define FMT_ASSERT(condition, message) \
+ ((condition) \
+ ? void() \
+ : fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
+# endif
+#endif
+
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
@@ -217,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
template <typename T> struct std_string_view {};
#endif
-// Casts nonnegative integer to unsigned.
+#ifdef FMT_USE_INT128
+// Do nothing.
+#elif defined(__SIZEOF_INT128__)
+# define FMT_USE_INT128 1
+using int128_t = __int128_t;
+using uint128_t = __uint128_t;
+#else
+# define FMT_USE_INT128 0
+#endif
+#if !FMT_USE_INT128
+struct int128_t {};
+struct uint128_t {};
+#endif
+
+// Casts a nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
@@ -225,6 +268,9 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
}
} // namespace internal
+template <typename... Ts>
+using void_t = typename internal::void_t_impl<Ts...>::type;
+
/**
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
subset of the API. ``fmt::basic_string_view`` is used for format strings even
@@ -258,10 +304,11 @@ template <typename Char> class basic_string_view {
: data_(s), size_(std::char_traits<Char>::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */
- template <typename Alloc>
- FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Alloc>& s)
- FMT_NOEXCEPT : data_(s.data()),
- size_(s.size()) {}
+ template <typename Traits, typename Alloc>
+ FMT_CONSTEXPR basic_string_view(
+ const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
+ : data_(s.data()),
+ size_(s.size()) {}
template <
typename S,
@@ -278,6 +325,8 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR iterator begin() const { return data_; }
FMT_CONSTEXPR iterator end() const { return data_ + size_; }
+ FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
+
FMT_CONSTEXPR void remove_prefix(size_t n) {
data_ += n;
size_ -= n;
@@ -349,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
return s;
}
-template <typename Char, typename Traits, typename Allocator>
+template <typename Char, typename Traits, typename Alloc>
inline basic_string_view<Char> to_string_view(
- const std::basic_string<Char, Traits, Allocator>& s) {
- return {s.data(), s.size()};
+ const std::basic_string<Char, Traits, Alloc>& s) {
+ return s;
}
template <typename Char>
@@ -381,7 +430,7 @@ constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) {
namespace internal {
void to_string_view(...);
-using fmt::v5::to_string_view;
+using fmt::v6::to_string_view;
// Specifies whether S is a string type convertible to fmt::basic_string_view.
// It should be a constexpr function but MSVC 2017 fails to compile it in
@@ -397,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
};
struct error_handler {
- FMT_CONSTEXPR error_handler() {}
- FMT_CONSTEXPR error_handler(const error_handler&) {}
+ FMT_CONSTEXPR error_handler() = default;
+ FMT_CONSTEXPR error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void on_error(const char* message);
@@ -408,10 +457,24 @@ struct error_handler {
/** String's character type. */
template <typename S> using char_t = typename internal::char_t_impl<S>::type;
-// Parsing context consisting of a format string range being parsed and an
-// argument counter for automatic indexing.
+/**
+ \rst
+ Parsing context consisting of a format string range being parsed and an
+ argument counter for automatic indexing.
+
+ You can use one of the following type aliases for common character types:
+
+ +-----------------------+-------------------------------------+
+ | Type | Definition |
+ +=======================+=====================================+
+ | format_parse_context | basic_format_parse_context<char> |
+ +-----------------------+-------------------------------------+
+ | wformat_parse_context | basic_format_parse_context<wchar_t> |
+ +-----------------------+-------------------------------------+
+ \endrst
+ */
template <typename Char, typename ErrorHandler = internal::error_handler>
-class basic_parse_context : private ErrorHandler {
+class basic_format_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
int next_arg_id_;
@@ -420,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
- explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
- ErrorHandler eh = ErrorHandler())
+ explicit FMT_CONSTEXPR basic_format_parse_context(
+ basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
- // Returns an iterator to the beginning of the format string range being
- // parsed.
+ /**
+ Returns an iterator to the beginning of the format string range being
+ parsed.
+ */
FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
return format_str_.begin();
}
- // Returns an iterator past the end of the format string range being parsed.
+ /**
+ Returns an iterator past the end of the format string range being parsed.
+ */
FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
- // Advances the begin iterator to ``it``.
+ /** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
format_str_.remove_prefix(internal::to_unsigned(it - begin()));
}
- // Returns the next argument index.
- FMT_CONSTEXPR unsigned next_arg_id() {
- if (next_arg_id_ >= 0) return internal::to_unsigned(next_arg_id_++);
+ /**
+ Reports an error if using the manual argument indexing; otherwise returns
+ the next argument index and switches to the automatic indexing.
+ */
+ FMT_CONSTEXPR int next_arg_id() {
+ if (next_arg_id_ >= 0) return next_arg_id_++;
on_error("cannot switch from manual to automatic argument indexing");
return 0;
}
- FMT_CONSTEXPR bool check_arg_id(unsigned) {
- if (next_arg_id_ > 0) {
+ /**
+ Reports an error if using the automatic argument indexing; otherwise
+ switches to the manual indexing.
+ */
+ FMT_CONSTEXPR void check_arg_id(int) {
+ if (next_arg_id_ > 0)
on_error("cannot switch from automatic to manual argument indexing");
- return false;
- }
- next_arg_id_ = -1;
- return true;
+ else
+ next_arg_id_ = -1;
}
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
@@ -463,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
};
-using format_parse_context = basic_parse_context<char>;
-using wformat_parse_context = basic_parse_context<wchar_t>;
+using format_parse_context = basic_format_parse_context<char>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
-using parse_context FMT_DEPRECATED = basic_parse_context<char>;
-using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>;
+template <typename Char, typename ErrorHandler = internal::error_handler>
+using basic_parse_context FMT_DEPRECATED_ALIAS =
+ basic_format_parse_context<Char, ErrorHandler>;
+using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
+using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
@@ -484,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
: bool_constant<!std::is_arithmetic<T>::value &&
std::is_convertible<T, int>::value> {};
-namespace internal {
-
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
+namespace internal {
+
/** A contiguous memory buffer with an optional growing ability. */
template <typename T> class buffer {
private:
- buffer(const buffer&) = delete;
- void operator=(const buffer&) = delete;
-
T* ptr_;
std::size_t size_;
std::size_t capacity_;
@@ -524,7 +596,9 @@ template <typename T> class buffer {
using value_type = T;
using const_reference = const T&;
- virtual ~buffer() {}
+ buffer(const buffer&) = delete;
+ void operator=(const buffer&) = delete;
+ virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@@ -618,10 +692,13 @@ enum type {
uint_type,
long_long_type,
ulong_long_type,
+ int128_type,
+ uint128_type,
bool_type,
char_type,
last_integer_type = char_type,
// followed by floating-point types.
+ float_type,
double_type,
long_double_type,
last_numeric_type = long_double_type,
@@ -644,22 +721,25 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_t, int128_type);
+FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
-FMT_CONSTEXPR bool is_integral(type t) {
- FMT_ASSERT(t != internal::named_arg_type, "invalid argument type");
- return t > internal::none_type && t <= internal::last_integer_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_CONSTEXPR bool is_arithmetic(type t) {
- FMT_ASSERT(t != internal::named_arg_type, "invalid argument type");
- return t > internal::none_type && t <= internal::last_numeric_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;
}
template <typename Char> struct string_value {
@@ -668,7 +748,7 @@ template <typename Char> struct string_value {
};
template <typename Context> struct custom_value {
- using parse_context = basic_parse_context<typename Context::char_type>;
+ using parse_context = basic_format_parse_context<typename Context::char_type>;
const void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
};
@@ -683,8 +763,11 @@ template <typename Context> class value {
unsigned uint_value;
long long long_long_value;
unsigned long long ulong_long_value;
+ int128_t int128_value;
+ uint128_t uint128_value;
bool bool_value;
char_type char_value;
+ float float_value;
double double_value;
long double long_double_value;
const void* pointer;
@@ -697,6 +780,9 @@ template <typename Context> class value {
FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) : long_long_value(val) {}
value(unsigned long long val) : ulong_long_value(val) {}
+ value(int128_t val) : int128_value(val) {}
+ value(uint128_t val) : uint128_value(val) {}
+ value(float val) : float_value(val) {}
value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {}
value(bool val) : bool_value(val) {}
@@ -724,9 +810,9 @@ template <typename Context> class value {
private:
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
- static void format_custom_arg(const void* arg,
- basic_parse_context<char_type>& parse_ctx,
- Context& ctx) {
+ static void format_custom_arg(
+ const void* arg, basic_format_parse_context<char_type>& parse_ctx,
+ Context& ctx) {
Formatter f;
parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
@@ -756,6 +842,8 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
FMT_CONSTEXPR long long map(long long val) { return val; }
FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
+ FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
+ FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
FMT_CONSTEXPR bool map(bool val) { return val; }
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
@@ -766,7 +854,7 @@ template <typename Context> struct arg_mapper {
return val;
}
- FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); }
+ FMT_CONSTEXPR float map(float val) { return val; }
FMT_CONSTEXPR double map(double val) { return val; }
FMT_CONSTEXPR long double map(long double val) { return val; }
@@ -785,6 +873,15 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
+ template <
+ typename T,
+ FMT_ENABLE_IF(
+ std::is_constructible<std_string_view<char_type>, T>::value &&
+ !std::is_constructible<basic_string_view<char_type>, T>::value &&
+ !is_string<T>::value)>
+ FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+ return std_string_view<char_type>(val);
+ }
FMT_CONSTEXPR const char* map(const signed char* val) {
static_assert(std::is_same<char_type, char>::value, "invalid string type");
return reinterpret_cast<const char*>(val);
@@ -810,11 +907,14 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
- FMT_CONSTEXPR int map(const T& val) {
- return static_cast<int>(val);
+ FMT_CONSTEXPR auto map(const T& val) -> decltype(
+ map(static_cast<typename std::underlying_type<T>::type>(val))) {
+ return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
+ !std::is_constructible<basic_string_view<char_type>,
+ T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
@@ -833,12 +933,13 @@ template <typename Context> struct arg_mapper {
// A type constant after applying arg_mapper<Context>.
template <typename T, typename Context>
using mapped_type_constant =
- type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())),
+ type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>;
+enum { packed_arg_bits = 5 };
// Maximum number of arguments with packed types.
-enum { max_packed_args = 15 };
-enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
+enum { max_packed_args = 63 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
template <typename Context> class arg_map;
} // namespace internal
@@ -869,7 +970,8 @@ template <typename Context> class basic_format_arg {
public:
explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
- void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const {
+ void format(basic_format_parse_context<char_type>& parse_ctx,
+ Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx);
}
@@ -885,8 +987,8 @@ template <typename Context> class basic_format_arg {
internal::type type() const { return type_; }
- bool is_integral() const { return internal::is_integral(type_); }
- bool is_arithmetic() const { return internal::is_arithmetic(type_); }
+ bool is_integral() const { return internal::is_integral_type(type_); }
+ bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
};
/**
@@ -915,10 +1017,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(arg.value_.long_long_value);
case internal::ulong_long_type:
return vis(arg.value_.ulong_long_value);
+#if FMT_USE_INT128
+ case internal::int128_type:
+ return vis(arg.value_.int128_value);
+ case internal::uint128_type:
+ return vis(arg.value_.uint128_value);
+#else
+ case internal::int128_type:
+ case internal::uint128_type:
+ break;
+#endif
case internal::bool_type:
return vis(arg.value_.bool_value);
case internal::char_type:
return vis(arg.value_.char_value);
+ case internal::float_type:
+ return vis(arg.value_.float_value);
case internal::double_type:
return vis(arg.value_.double_value);
case internal::long_double_type:
@@ -936,20 +1050,10 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(monostate());
}
-template <typename Visitor, typename Context>
-FMT_DEPRECATED FMT_CONSTEXPR auto visit(Visitor&& vis,
- const basic_format_arg<Context>& arg)
- -> decltype(vis(0)) {
- return visit_format_arg(std::forward<Visitor>(vis), arg);
-}
-
namespace internal {
// A map from argument names to their values for named arguments.
template <typename Context> class arg_map {
private:
- arg_map(const arg_map&) = delete;
- void operator=(const arg_map&) = delete;
-
using char_type = typename Context::char_type;
struct entry {
@@ -967,6 +1071,8 @@ template <typename Context> class arg_map {
}
public:
+ arg_map(const arg_map&) = delete;
+ void operator=(const arg_map&) = delete;
arg_map() : map_(nullptr), size_(0) {}
void init(const basic_format_args<Context>& args);
~arg_map() { delete[] map_; }
@@ -989,6 +1095,8 @@ class locale_ref {
locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
+ explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
+
template <typename Locale> Locale get() const;
};
@@ -997,7 +1105,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value |
- (encode_types<Context, Args...>() << 4);
+ (encode_types<Context, Args...>() << packed_arg_bits);
}
template <typename Context, typename T>
@@ -1033,14 +1141,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal::arg_map<basic_format_context> map_;
internal::locale_ref loc_;
- basic_format_context(const basic_format_context&) = delete;
- void operator=(const basic_format_context&) = delete;
-
public:
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>;
template <typename T> using formatter_type = formatter<T, char_type>;
+ basic_format_context(const basic_format_context&) = delete;
+ void operator=(const basic_format_context&) = delete;
/**
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
@@ -1050,7 +1157,7 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal::locale_ref loc = internal::locale_ref())
: out_(out), args_(ctx_args), loc_(loc) {}
- format_arg arg(unsigned id) const { return args_.get(id); }
+ format_arg arg(int id) const { return args_.get(id); }
// Checks if manual indexing is used and returns the argument with the
// specified name.
@@ -1061,7 +1168,6 @@ template <typename OutputIt, typename Char> class basic_format_context {
// Returns an iterator to the beginning of the output range.
iterator out() { return out_; }
- FMT_DEPRECATED iterator begin() { return out_; }
// Advances the begin iterator to ``it``.
void advance_to(iterator it) { out_ = it; }
@@ -1092,7 +1198,7 @@ template <typename Context, typename... Args> class format_arg_store {
basic_format_arg<Context>>;
// If the arguments are not packed, add one more element to mark the end.
- value_type data_[num_args + (!is_packed || num_args == 0 ? 1 : 0)];
+ value_type data_[num_args + (num_args == 0 ? 1 : 0)];
friend class basic_format_args<Context>;
@@ -1100,7 +1206,6 @@ template <typename Context, typename... Args> class format_arg_store {
static constexpr unsigned long long types =
is_packed ? internal::encode_types<Context, Args...>()
: internal::is_unpacked_bit | num_args;
- FMT_DEPRECATED static constexpr unsigned long long TYPES = types;
format_arg_store(const Args&... args)
: data_{internal::make_arg<is_packed, Context>(args)...} {}
@@ -1123,7 +1228,7 @@ inline format_arg_store<Context, Args...> make_format_args(
/** Formatting arguments. */
template <typename Context> class basic_format_args {
public:
- using size_type = unsigned;
+ using size_type = int;
using format_arg = basic_format_arg<Context>;
private:
@@ -1142,9 +1247,10 @@ template <typename Context> class basic_format_args {
bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
- internal::type type(unsigned index) const {
- unsigned shift = index * 4;
- return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
+ internal::type type(int index) const {
+ int shift = index * internal::packed_arg_bits;
+ unsigned int mask = (1 << internal::packed_arg_bits) - 1;
+ return static_cast<internal::type>((types_ >> shift) & mask);
}
friend class internal::arg_map<Context>;
@@ -1152,7 +1258,7 @@ template <typename Context> class basic_format_args {
void set_data(const internal::value<Context>* values) { values_ = values; }
void set_data(const format_arg* args) { args_ = args; }
- format_arg do_get(size_type index) const {
+ format_arg do_get(int index) const {
format_arg arg;
if (!is_packed()) {
auto num_args = max_size();
@@ -1186,23 +1292,23 @@ template <typename Context> class basic_format_args {
Constructs a `basic_format_args` object from a dynamic set of arguments.
\endrst
*/
- basic_format_args(const format_arg* args, size_type count)
- : types_(internal::is_unpacked_bit | count) {
+ basic_format_args(const format_arg* args, int count)
+ : types_(internal::is_unpacked_bit | internal::to_unsigned(count)) {
set_data(args);
}
/** Returns the argument at specified index. */
- format_arg get(size_type index) const {
+ format_arg get(int index) const {
format_arg arg = do_get(index);
if (arg.type_ == internal::named_arg_type)
arg = arg.value_.named_arg->template deserialize<Context>();
return arg;
}
- size_type max_size() const {
+ int max_size() const {
unsigned long long max_packed = internal::max_packed_args;
- return static_cast<size_type>(
- is_packed() ? max_packed : types_ & ~internal::is_unpacked_bit);
+ return static_cast<int>(is_packed() ? max_packed
+ : types_ & ~internal::is_unpacked_bit);
}
};
@@ -1258,14 +1364,30 @@ template <typename T, typename Char> struct named_arg : named_arg_base<Char> {
};
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
-inline void check_format_string(const S&) {}
+inline void check_format_string(const S&) {
+#if defined(FMT_ENFORCE_COMPILE_STRING)
+ static_assert(is_compile_string<S>::value,
+ "FMT_ENFORCE_COMPILE_STRING requires all format strings to "
+ "utilize FMT_STRING() or fmt().");
+#endif
+}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
-template <typename S, typename... Args, typename Char = char_t<S>>
-inline format_arg_store<buffer_context<Char>, Args...> make_args_checked(
- const S& format_str, const Args&... args) {
- check_format_string<Args...>(format_str);
+struct view {};
+template <bool...> struct bool_pack;
+template <bool... Args>
+using all_true =
+ std::is_same<bool_pack<Args..., true>, bool_pack<true, Args...>>;
+
+template <typename... Args, typename S, typename Char = char_t<S>>
+inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
+make_args_checked(const S& format_str,
+ const remove_reference_t<Args>&... args) {
+ static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() ||
+ !std::is_reference<Args>())...>::value,
+ "passing views as lvalues is disallowed");
+ check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
return {args...};
}
@@ -1323,9 +1445,10 @@ template <typename Container, typename S, typename... Args,
is_contiguous<Container>::value&& internal::is_string<S>::value)>
inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str,
- const Args&... args) {
- return vformat_to(out, to_string_view(format_str),
- {internal::make_args_checked(format_str, args...)});
+ Args&&... args) {
+ return vformat_to(
+ out, to_string_view(format_str),
+ {internal::make_args_checked<Args...>(format_str, args...)});
}
template <typename S, typename Char = char_t<S>>
@@ -1347,14 +1470,14 @@ inline std::basic_string<Char> vformat(
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>>
-inline std::basic_string<Char> format(const S& format_str,
- const Args&... args) {
- return internal::vformat(to_string_view(format_str),
- {internal::make_args_checked(format_str, args...)});
+inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
+ return internal::vformat(
+ to_string_view(format_str),
+ {internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
-FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
+FMT_API void vprint(string_view format_str, format_args args);
/**
\rst
@@ -1369,14 +1492,11 @@ FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
-inline void print(std::FILE* f, const S& format_str, const Args&... args) {
+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...));
+ internal::make_args_checked<Args...>(format_str, args...));
}
-FMT_API void vprint(string_view format_str, format_args args);
-FMT_API void vprint(wstring_view format_str, wformat_args args);
-
/**
\rst
Prints formatted data to ``stdout``.
@@ -1388,9 +1508,9 @@ FMT_API void vprint(wstring_view format_str, wformat_args args);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
-inline void print(const S& format_str, const Args&... args) {
+inline void print(const S& format_str, Args&&... args) {
vprint(to_string_view(format_str),
- internal::make_args_checked(format_str, args...));
+ internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
diff --git a/src/third_party/fmt/dist/include/fmt/format-inl.h b/src/third_party/fmt/dist/include/fmt/format-inl.h
index cce1a34a426..72b304665df 100644
--- a/src/third_party/fmt/dist/include/fmt/format-inl.h
+++ b/src/third_party/fmt/dist/include/fmt/format-inl.h
@@ -1,4 +1,4 @@
-// Formatting library for C++
+// Formatting library for C++ - implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@@ -10,15 +10,13 @@
#include "format.h"
-#include <string.h>
-
+#include <cassert>
#include <cctype>
-#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
-#include <cstddef> // for std::ptrdiff_t
#include <cstring> // for std::memmove
+#include <cwchar>
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
#endif
@@ -46,25 +44,22 @@
#ifdef _MSC_VER
# pragma warning(push)
-# pragma warning(disable : 4127) // conditional expression is constant
# pragma warning(disable : 4702) // unreachable code
-// Disable deprecation warning for strerror. The latter is not called but
-// MSVC fails to detect it.
-# pragma warning(disable : 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
-inline fmt::internal::null<> strerror_r(int, char*, ...) {
- return fmt::internal::null<>();
-}
-inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) {
- return fmt::internal::null<>();
-}
+inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
+inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
FMT_BEGIN_NAMESPACE
namespace internal {
+FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
+ print(stderr, "{}:{}: assertion failed: {}", file, line, message);
+ std::abort();
+}
+
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
@@ -78,9 +73,9 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
-typedef void (*FormatFunc)(internal::buffer<char>&, int, string_view);
+using format_func = void (*)(internal::buffer<char>&, int, string_view);
-// Portable thread-safe version of strerror.
+// 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,
// or a pointer to some static immutable string.
@@ -157,14 +152,13 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
- typedef internal::int_traits<int>::main_type main_type;
- main_type abs_value = static_cast<main_type>(error_code);
+ auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
- writer w(out);
+ internal::writer w(out);
if (message.size() <= inline_buffer_size - error_code_size) {
w.write(message);
w.write(SEP);
@@ -174,7 +168,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
assert(out.size() <= inline_buffer_size);
}
-// try an fwrite, FMT_THROW on failure
+// 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);
@@ -183,13 +177,12 @@ FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
}
}
-FMT_FUNC void report_error(FormatFunc func, int error_code,
+FMT_FUNC void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT {
memory_buffer full_message;
func(full_message, error_code, message);
- // Use Writer::data instead of Writer::c_str to avoid potential memory
- // allocation.
- fwrite_fully(full_message.data(), 1, full_message.size(), stderr);
+ // Don't use fwrite_fully because the latter may throw.
+ (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace internal
@@ -207,20 +200,35 @@ template <typename Locale> Locale locale_ref::get() const {
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
}
+template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) {
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping();
+}
template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.thousands_sep();
}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+ .decimal_point();
+}
} // namespace internal
#else
template <typename Char>
+FMT_FUNC std::string internal::grouping_impl(locale_ref) {
+ return "\03";
+}
+template <typename Char>
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR;
}
+template <typename Char>
+FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
+ return '.';
+}
#endif
-FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {}
-FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT {}
+FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
+FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default;
FMT_FUNC void system_error::init(int err_code, string_view format_str,
format_args args) {
@@ -234,7 +242,7 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
namespace internal {
template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
- // Assume little endian; pointer formatting is implementation-defined anyway.
+ // fallback_uintptr is always stored in little endian.
int i = static_cast<int>(sizeof(void*)) - 1;
while (i > 0 && n.value[i] == 0) --i;
auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
@@ -242,16 +250,7 @@ template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
}
template <typename T>
-int format_float(char* buf, std::size_t size, const char* format, int precision,
- T value) {
- // 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);
-}
-
-template <typename T>
-const char basic_data<T>::DIGITS[] =
+const char basic_data<T>::digits[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
@@ -259,31 +258,31 @@ const char basic_data<T>::DIGITS[] =
"8081828384858687888990919293949596979899";
template <typename T>
-const char basic_data<T>::HEX_DIGITS[] = "0123456789abcdef";
+const char basic_data<T>::hex_digits[] = "0123456789abcdef";
#define FMT_POWERS_OF_10(factor) \
- factor * 10, factor * 100, factor * 1000, factor * 10000, factor * 100000, \
- factor * 1000000, factor * 10000000, factor * 100000000, \
- factor * 1000000000
+ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
+ (factor)*1000000, (factor)*10000000, (factor)*100000000, \
+ (factor)*1000000000
template <typename T>
-const uint64_t basic_data<T>::POWERS_OF_10_64[] = {
- 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
- 10000000000000000000ull};
+const uint64_t basic_data<T>::powers_of_10_64[] = {
+ 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+ 10000000000000000000ULL};
template <typename T>
-const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {0,
+const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
FMT_POWERS_OF_10(1)};
template <typename T>
-const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
- 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
- 10000000000000000000ull};
+const uint64_t basic_data<T>::zero_or_powers_of_10_64[] = {
+ 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+ 10000000000000000000ULL};
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
// These are generated by support/compute-powers.py.
template <typename T>
-const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
+const uint64_t basic_data<T>::pow10_significands[] = {
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
@@ -318,7 +317,7 @@ const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
// to significands above.
template <typename T>
-const int16_t basic_data<T>::POW10_EXPONENTS[] = {
+const int16_t basic_data<T>::pow10_exponents[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
@@ -329,21 +328,33 @@ const int16_t basic_data<T>::POW10_EXPONENTS[] = {
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
template <typename T>
-const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
+const char basic_data<T>::foreground_color[] = "\x1b[38;2;";
template <typename T>
-const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
-template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
-template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
+const char basic_data<T>::background_color[] = "\x1b[48;2;";
+template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
+template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
+template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value =
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
};
+class fp;
+template <int SHIFT = 0> fp normalize(fp value);
+
+// Lower (upper) boundary is a value half way between a floating-point value
+// and its predecessor (successor). Boundaries have the same exponent as the
+// value so only significands are stored.
+struct boundaries {
+ uint64_t lower;
+ uint64_t upper;
+};
+
// A handmade floating-point number f * pow(2, e).
class fp {
private:
- typedef uint64_t significand_type;
+ using significand_type = uint64_t;
// All sizes are in bits.
// Subtract 1 to account for an implicit most significant bit in the
@@ -351,7 +362,7 @@ class fp {
static FMT_CONSTEXPR_DECL const int double_significand_size =
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
- 1ull << double_significand_size;
+ 1ULL << double_significand_size;
public:
significand_type f;
@@ -365,95 +376,377 @@ class fp {
// Constructs fp from an IEEE754 double. It is a template to prevent compile
// errors on platforms where double is not IEEE754.
- template <typename Double> explicit fp(Double d) {
+ template <typename Double> explicit fp(Double d) { assign(d); }
+
+ // Normalizes the value converted from double and multiplied by (1 << SHIFT).
+ template <int SHIFT> friend fp normalize(fp value) {
+ // Handle subnormals.
+ const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
+ while ((value.f & shifted_implicit_bit) == 0) {
+ value.f <<= 1;
+ --value.e;
+ }
+ // Subtract 1 to account for hidden bit.
+ const auto offset =
+ fp::significand_size - fp::double_significand_size - SHIFT - 1;
+ value.f <<= offset;
+ value.e -= offset;
+ return value;
+ }
+
+ // Assigns d to this and return true iff predecessor is closer than successor.
+ template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
+ bool assign(Double d) {
// Assume double is in the format [sign][exponent][significand].
- typedef std::numeric_limits<Double> limits;
+ using limits = std::numeric_limits<Double>;
const int exponent_size =
bits<Double>::value - double_significand_size - 1; // -1 for sign
const uint64_t significand_mask = implicit_bit - 1;
- const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
+ const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d);
- auto biased_e = (u & exponent_mask) >> double_significand_size;
f = u & significand_mask;
+ auto biased_e = (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;
if (biased_e != 0)
f += implicit_bit;
else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
+ return is_predecessor_closer;
}
- // Normalizes the value converted from double and multiplied by (1 << SHIFT).
- template <int SHIFT = 0> void normalize() {
- // Handle subnormals.
- auto shifted_implicit_bit = implicit_bit << SHIFT;
- while ((f & shifted_implicit_bit) == 0) {
- f <<= 1;
- --e;
- }
- // Subtract 1 to account for hidden bit.
- auto offset = significand_size - double_significand_size - SHIFT - 1;
- f <<= offset;
- e -= offset;
+ template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
+ bool assign(Double) {
+ *this = fp();
+ return false;
}
- // Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
- // a boundary is a value half way between the number and its predecessor
+ // Assigns d to this together with computing lower and upper boundaries,
+ // where a boundary is a value half way between the number and its predecessor
// (lower) or successor (upper). The upper boundary is normalized and lower
// has the same exponent but may be not normalized.
- void compute_boundaries(fp& lower, fp& upper) const {
- lower =
- f == implicit_bit ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
- upper = fp((f << 1) + 1, e - 1);
- upper.normalize<1>(); // 1 is to account for the exponent shift above.
+ template <typename Double> boundaries assign_with_boundaries(Double d) {
+ bool is_lower_closer = assign(d);
+ fp lower =
+ is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
+ // 1 in normalize accounts for the exponent shift above.
+ fp upper = normalize<1>(fp((f << 1) + 1, e - 1));
lower.f <<= lower.e - upper.e;
- lower.e = upper.e;
+ return boundaries{lower.f, upper.f};
+ }
+
+ template <typename Double> boundaries assign_float_with_boundaries(Double d) {
+ assign(d);
+ constexpr int min_normal_e = std::numeric_limits<float>::min_exponent -
+ std::numeric_limits<double>::digits;
+ significand_type half_ulp = 1 << (std::numeric_limits<double>::digits -
+ std::numeric_limits<float>::digits - 1);
+ if (min_normal_e > e) half_ulp <<= min_normal_e - e;
+ fp upper = normalize<0>(fp(f + half_ulp, e));
+ fp lower = fp(
+ f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e);
+ lower.f <<= lower.e - upper.e;
+ return boundaries{lower.f, upper.f};
}
};
-// Returns an fp number representing x - y. Result may not be normalized.
-inline fp operator-(fp x, fp y) {
- FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
- return fp(x.f - y.f, x.e);
-}
+inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
-// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
-// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be
-// normalized.
-FMT_FUNC fp operator*(fp x, fp y) {
- int exp = x.e + y.e + 64;
+// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
+inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
#if FMT_USE_INT128
- auto product = static_cast<__uint128_t>(x.f) * y.f;
+ auto product = static_cast<__uint128_t>(lhs) * rhs;
auto f = static_cast<uint64_t>(product >> 64);
- if ((static_cast<uint64_t>(product) & (1ULL << 63)) != 0) ++f;
- return fp(f, exp);
+ return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
#else
// Multiply 32-bit parts of significands.
uint64_t mask = (1ULL << 32) - 1;
- uint64_t a = x.f >> 32, b = x.f & mask;
- uint64_t c = y.f >> 32, d = y.f & mask;
+ uint64_t a = lhs >> 32, b = lhs & mask;
+ uint64_t c = rhs >> 32, d = rhs & mask;
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
// Compute mid 64-bit of result and round.
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
- return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), exp);
+ return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
#endif
}
-// Returns 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.
+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 double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
+ const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
int index = static_cast<int>(
- std::ceil((min_exponent + fp::significand_size - 1) * one_over_log2_10));
+ static_cast<int64_t>(
+ (min_exponent + fp::significand_size - 1) * one_over_log2_10 +
+ ((uint64_t(1) << 32) - 1) // ceil
+ ) >>
+ 32 // arithmetic shift
+ );
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
// Difference between 2 consecutive decimal exponents in cached powers of 10.
const int dec_exp_step = 8;
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
pow10_exponent = first_dec_exp + index * dec_exp_step;
- return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
+ return {data::pow10_significands[index], data::pow10_exponents[index]};
}
+// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
+// is not available.
+struct accumulator {
+ uint64_t lower;
+ uint64_t upper;
+
+ accumulator() : lower(0), upper(0) {}
+ explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
+
+ void operator+=(uint64_t n) {
+ lower += n;
+ if (lower < n) ++upper;
+ }
+ void operator>>=(int shift) {
+ assert(shift == 32);
+ (void)shift;
+ lower = (upper << 32) | (lower >> 32);
+ upper >>= 32;
+ }
+};
+
+class bigint {
+ private:
+ // A bigint is stored as an array of bigits (big digits), with bigit at index
+ // 0 being the least significant one.
+ using bigit = uint32_t;
+ using double_bigit = uint64_t;
+ enum { bigits_capacity = 32 };
+ basic_memory_buffer<bigit, bigits_capacity> bigits_;
+ int exp_;
+
+ static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
+
+ friend struct formatter<bigint>;
+
+ void subtract_bigits(int index, bigit other, bigit& borrow) {
+ auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
+ bigits_[index] = static_cast<bigit>(result);
+ borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+ }
+
+ void remove_leading_zeros() {
+ int num_bigits = static_cast<int>(bigits_.size()) - 1;
+ while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
+ bigits_.resize(num_bigits + 1);
+ }
+
+ // Computes *this -= other assuming aligned bigints and *this >= other.
+ void subtract_aligned(const bigint& other) {
+ FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+ FMT_ASSERT(compare(*this, other) >= 0, "");
+ bigit borrow = 0;
+ int i = other.exp_ - exp_;
+ for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
+ ++i, ++j) {
+ subtract_bigits(i, other.bigits_[j], borrow);
+ }
+ while (borrow > 0) subtract_bigits(i, 0, borrow);
+ remove_leading_zeros();
+ }
+
+ void multiply(uint32_t value) {
+ const double_bigit wide_value = value;
+ bigit carry = 0;
+ for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+ double_bigit result = bigits_[i] * wide_value + carry;
+ bigits_[i] = static_cast<bigit>(result);
+ carry = static_cast<bigit>(result >> bigit_bits);
+ }
+ if (carry != 0) bigits_.push_back(carry);
+ }
+
+ void multiply(uint64_t value) {
+ const bigit mask = ~bigit(0);
+ const double_bigit lower = value & mask;
+ const double_bigit upper = value >> bigit_bits;
+ double_bigit carry = 0;
+ for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+ double_bigit result = bigits_[i] * lower + (carry & mask);
+ carry =
+ bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
+ bigits_[i] = static_cast<bigit>(result);
+ }
+ while (carry != 0) {
+ bigits_.push_back(carry & mask);
+ carry >>= bigit_bits;
+ }
+ }
+
+ public:
+ bigint() : exp_(0) {}
+ explicit bigint(uint64_t n) { assign(n); }
+ ~bigint() { assert(bigits_.capacity() <= bigits_capacity); }
+
+ bigint(const bigint&) = delete;
+ void operator=(const bigint&) = delete;
+
+ void assign(const bigint& other) {
+ bigits_.resize(other.bigits_.size());
+ auto data = other.bigits_.data();
+ std::copy(data, data + other.bigits_.size(), bigits_.data());
+ exp_ = other.exp_;
+ }
+
+ void assign(uint64_t n) {
+ int num_bigits = 0;
+ do {
+ bigits_[num_bigits++] = n & ~bigit(0);
+ n >>= bigit_bits;
+ } while (n != 0);
+ bigits_.resize(num_bigits);
+ exp_ = 0;
+ }
+
+ int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
+
+ bigint& operator<<=(int shift) {
+ assert(shift >= 0);
+ exp_ += shift / bigit_bits;
+ shift %= bigit_bits;
+ if (shift == 0) return *this;
+ bigit carry = 0;
+ for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+ bigit c = bigits_[i] >> (bigit_bits - shift);
+ bigits_[i] = (bigits_[i] << shift) + carry;
+ carry = c;
+ }
+ if (carry != 0) bigits_.push_back(carry);
+ return *this;
+ }
+
+ template <typename Int> bigint& operator*=(Int value) {
+ FMT_ASSERT(value > 0, "");
+ multiply(uint32_or_64_or_128_t<Int>(value));
+ return *this;
+ }
+
+ friend int compare(const bigint& lhs, const bigint& rhs) {
+ int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+ if (num_lhs_bigits != num_rhs_bigits)
+ return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+ int i = static_cast<int>(lhs.bigits_.size()) - 1;
+ int j = static_cast<int>(rhs.bigits_.size()) - 1;
+ 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];
+ if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+ }
+ if (i != j) return i > j ? 1 : -1;
+ return 0;
+ }
+
+ // Returns compare(lhs1 + lhs2, rhs).
+ friend int add_compare(const bigint& lhs1, const bigint& lhs2,
+ const bigint& rhs) {
+ int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
+ int num_rhs_bigits = rhs.num_bigits();
+ 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;
+ };
+ double_bigit borrow = 0;
+ int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
+ for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
+ double_bigit sum =
+ static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+ bigit rhs_bigit = get_bigit(rhs, i);
+ if (sum > rhs_bigit + borrow) return 1;
+ borrow = rhs_bigit + borrow - sum;
+ if (borrow > 1) return -1;
+ borrow <<= bigit_bits;
+ }
+ return borrow != 0 ? -1 : 0;
+ }
+
+ // Assigns pow(10, exp) to this bigint.
+ void assign_pow10(int exp) {
+ assert(exp >= 0);
+ if (exp == 0) return assign(1);
+ // Find the top bit.
+ int bitmask = 1;
+ while (exp >= bitmask) bitmask <<= 1;
+ bitmask >>= 1;
+ // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+ // repeated squaring and multiplication.
+ assign(5);
+ bitmask >>= 1;
+ while (bitmask != 0) {
+ square();
+ if ((exp & bitmask) != 0) *this *= 5;
+ bitmask >>= 1;
+ }
+ *this <<= exp; // Multiply by pow(2, exp) by shifting.
+ }
+
+ void square() {
+ basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+ int num_bigits = static_cast<int>(bigits_.size());
+ int num_result_bigits = 2 * num_bigits;
+ bigits_.resize(num_result_bigits);
+ using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
+ auto sum = accumulator_t();
+ for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
+ // Compute bigit at position bigit_index of the result by adding
+ // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+ for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
+ // Most terms are multiplied twice which can be optimized in the future.
+ sum += static_cast<double_bigit>(n[i]) * n[j];
+ }
+ bigits_[bigit_index] = static_cast<bigit>(sum);
+ sum >>= bits<bigit>::value; // Compute the carry.
+ }
+ // Do the same for the top half.
+ for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
+ ++bigit_index) {
+ for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+ sum += static_cast<double_bigit>(n[i++]) * n[j--];
+ bigits_[bigit_index] = static_cast<bigit>(sum);
+ sum >>= bits<bigit>::value;
+ }
+ --num_result_bigits;
+ remove_leading_zeros();
+ exp_ *= 2;
+ }
+
+ // Divides this bignum by divisor, assigning the remainder to this and
+ // returning the quotient.
+ int divmod_assign(const bigint& divisor) {
+ FMT_ASSERT(this != &divisor, "");
+ if (compare(*this, divisor) < 0) return 0;
+ int num_bigits = static_cast<int>(bigits_.size());
+ FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 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);
+ 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);
+ exp_ -= exp_difference;
+ }
+ int quotient = 0;
+ do {
+ subtract_aligned(divisor);
+ ++quotient;
+ } while (compare(*this, divisor) >= 0);
+ return quotient;
+ }
+};
+
enum round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
@@ -488,63 +781,58 @@ enum result {
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
template <typename Handler>
-digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
- Handler& handler) {
- fp one(1ull << -value.e, value.e);
+FMT_ALWAYS_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
// to normalization) - 1, shifted right by at most 60 bits.
- uint32_t integral = static_cast<uint32_t>(value.f >> -one.e);
+ auto integral = static_cast<uint32_t>(value.f >> -one.e);
FMT_ASSERT(integral != 0, "");
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.
// Divide by 10 to prevent overflow.
- auto result = handler.on_start(data::POWERS_OF_10_64[exp - 1] << -one.e,
+ auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
value.f / 10, error * 10, exp);
if (result != digits::more) return result;
// Generate digits for the integral part. This can produce up to 10 digits.
do {
uint32_t digit = 0;
- // This optimization by miloyip reduces the number of integer divisions by
+ auto divmod_integral = [&](uint32_t divisor) {
+ digit = integral / divisor;
+ integral %= divisor;
+ };
+ // This optimization by Milo Yip reduces the number of integer divisions by
// one per iteration.
switch (exp) {
case 10:
- digit = integral / 1000000000;
- integral %= 1000000000;
+ divmod_integral(1000000000);
break;
case 9:
- digit = integral / 100000000;
- integral %= 100000000;
+ divmod_integral(100000000);
break;
case 8:
- digit = integral / 10000000;
- integral %= 10000000;
+ divmod_integral(10000000);
break;
case 7:
- digit = integral / 1000000;
- integral %= 1000000;
+ divmod_integral(1000000);
break;
case 6:
- digit = integral / 100000;
- integral %= 100000;
+ divmod_integral(100000);
break;
case 5:
- digit = integral / 10000;
- integral %= 10000;
+ divmod_integral(10000);
break;
case 4:
- digit = integral / 1000;
- integral %= 1000;
+ divmod_integral(1000);
break;
case 3:
- digit = integral / 100;
- integral %= 100;
+ divmod_integral(100);
break;
case 2:
- digit = integral / 10;
- integral %= 10;
+ divmod_integral(10);
break;
case 1:
digit = integral;
@@ -557,7 +845,7 @@ digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
uint64_t remainder =
(static_cast<uint64_t>(integral) << -one.e) + fractional;
result = handler.on_digit(static_cast<char>('0' + digit),
- data::POWERS_OF_10_64[exp] << -one.e, remainder,
+ data::powers_of_10_64[exp] << -one.e, remainder,
error, exp, true);
if (result != digits::more) return result;
} while (exp > 0);
@@ -592,6 +880,7 @@ struct fixed_handler {
// Check if precision is satisfied just by leading zeros, e.g.
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
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';
@@ -627,7 +916,7 @@ struct fixed_handler {
};
// The shortest representation digit handler.
-template <int GRISU_VERSION> struct grisu_shortest_handler {
+struct grisu_shortest_handler {
char* buf;
int size;
// Distance between scaled value and upper bound (wp_W in Grisu3).
@@ -653,12 +942,7 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
uint64_t error, int exp, bool integral) {
buf[size++] = digit;
if (remainder >= error) return digits::more;
- if (GRISU_VERSION != 3) {
- uint64_t d = integral ? diff : diff * data::POWERS_OF_10_64[-exp];
- round(d, divisor, remainder, error);
- return digits::done;
- }
- uint64_t unit = integral ? 1 : data::POWERS_OF_10_64[-exp];
+ uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp];
uint64_t up = (diff - 1) * unit; // wp_Wup
round(up, divisor, remainder, error);
uint64_t down = (diff + 1) * unit; // wp_Wdown
@@ -673,149 +957,289 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
}
};
-template <typename Double,
- enable_if_t<(sizeof(Double) == sizeof(uint64_t)), int>>
-FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
- unsigned options, int& exp) {
+// Formats value using a variation of the Fixed-Precision Positive
+// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
+// https://fmt.dev/p372-steele.pdf.
+template <typename Double>
+void fallback_format(Double d, buffer<char>& buf, int& exp10) {
+ bigint numerator; // 2 * R in (FPP)^2.
+ bigint denominator; // 2 * S in (FPP)^2.
+ // lower and upper are differences between value and corresponding boundaries.
+ bigint lower; // (M^- in (FPP)^2).
+ bigint upper_store; // upper's value if different from lower.
+ bigint* upper = nullptr; // (M^+ in (FPP)^2).
+ fp value;
+ // Shift numerator and denominator by an extra bit or two (if lower boundary
+ // is closer) to make lower and upper integers. This eliminates multiplication
+ // by 2 during later computations.
+ // TODO: handle float
+ int shift = value.assign(d) ? 2 : 1;
+ uint64_t significand = value.f << shift;
+ if (value.e >= 0) {
+ numerator.assign(significand);
+ numerator <<= value.e;
+ lower.assign(1);
+ lower <<= value.e;
+ if (shift != 1) {
+ upper_store.assign(1);
+ upper_store <<= value.e + 1;
+ upper = &upper_store;
+ }
+ denominator.assign_pow10(exp10);
+ denominator <<= 1;
+ } else if (exp10 < 0) {
+ numerator.assign_pow10(-exp10);
+ lower.assign(numerator);
+ if (shift != 1) {
+ upper_store.assign(numerator);
+ upper_store <<= 1;
+ upper = &upper_store;
+ }
+ numerator *= significand;
+ denominator.assign(1);
+ denominator <<= shift - value.e;
+ } else {
+ numerator.assign(significand);
+ denominator.assign_pow10(exp10);
+ denominator <<= shift - value.e;
+ lower.assign(1);
+ if (shift != 1) {
+ upper_store.assign(1ULL << 1);
+ upper = &upper_store;
+ }
+ }
+ if (!upper) upper = &lower;
+ // Invariant: value == (numerator / denominator) * pow(10, exp10).
+ bool even = (value.f & 1) == 0;
+ int num_digits = 0;
+ char* data = buf.data();
+ for (;;) {
+ int digit = numerator.divmod_assign(denominator);
+ bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
+ // numerator + upper >[=] pow10:
+ bool high = add_compare(numerator, *upper, denominator) + even > 0;
+ data[num_digits++] = static_cast<char>('0' + digit);
+ if (low || high) {
+ if (!low) {
+ ++data[num_digits - 1];
+ } else if (high) {
+ int result = add_compare(numerator, numerator, denominator);
+ // Round half to even.
+ if (result > 0 || (result == 0 && (digit % 2) != 0))
+ ++data[num_digits - 1];
+ }
+ buf.resize(num_digits);
+ exp10 -= num_digits - 1;
+ return;
+ }
+ numerator *= 10;
+ lower *= 10;
+ if (upper != &lower) *upper *= 10;
+ }
+}
+
+// Formats value using the Grisu algorithm
+// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
+// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
+ static_assert(!std::is_same<T, float>(), "");
FMT_ASSERT(value >= 0, "value is negative");
- bool fixed = (options & grisu_options::fixed) != 0;
+
+ const bool fixed = specs.format == float_format::fixed;
if (value <= 0) { // <= instead of == to silence a warning.
- if (precision < 0 || !fixed) {
- exp = 0;
+ if (precision <= 0 || !fixed) {
buf.push_back('0');
- } else {
- exp = -precision;
- buf.resize(precision);
- std::uninitialized_fill_n(buf.data(), precision, '0');
+ return 0;
}
- return true;
+ buf.resize(to_unsigned(precision));
+ std::uninitialized_fill_n(buf.data(), precision, '0');
+ return -precision;
}
- fp fp_value(value);
+ if (!specs.use_grisu) return snprintf_float(value, precision, specs, 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 false;
- fp_value.normalize();
- auto cached_pow = get_cached_power(
- min_exp - (fp_value.e + fp::significand_size), cached_exp10);
- fp_value = fp_value * cached_pow;
+ 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(fp_value, 1, exp, handler) == digits::error)
- return false;
- buf.resize(to_unsigned(handler.size));
+ if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
+ return snprintf_float(value, precision, specs, buf);
+ int num_digits = handler.size;
+ if (!fixed) {
+ // Remove trailing zeros.
+ while (num_digits > 0 && buf[num_digits - 1] == '0') {
+ --num_digits;
+ ++exp;
+ }
+ }
+ buf.resize(to_unsigned(num_digits));
} else {
- fp lower, upper; // w^- and w^+ in the Grisu paper.
- fp_value.compute_boundaries(lower, upper);
- // Find a cached power of 10 such that multiplying upper by it will bring
+ fp fp_value;
+ auto boundaries = specs.binary32
+ ? fp_value.assign_float_with_boundaries(value)
+ : fp_value.assign_with_boundaries(value);
+ fp_value = normalize(fp_value);
+ // Find a cached power of 10 such that multiplying value by it will bring
// the exponent in the range [min_exp, -32].
- auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
- min_exp - (upper.e + fp::significand_size), cached_exp10);
- fp_value.normalize();
+ const fp cached_pow = get_cached_power(
+ min_exp - (fp_value.e + fp::significand_size), cached_exp10);
+ // Multiply value and boundaries by the cached power of 10.
fp_value = fp_value * cached_pow;
- lower = lower * cached_pow; // \tilde{M}^- in Grisu.
- upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
- assert(min_exp <= upper.e && upper.e <= -32);
- auto result = digits::result();
- int size = 0;
- if ((options & grisu_options::grisu3) != 0) {
- --lower.f; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
- ++upper.f; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
- // Numbers outside of (lower, upper) definitely do not round to value.
- grisu_shortest_handler<3> handler{buf.data(), 0, (upper - fp_value).f};
- result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
- size = handler.size;
- } else {
- ++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
- --upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
- grisu_shortest_handler<2> handler{buf.data(), 0, (upper - fp_value).f};
- result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
- size = handler.size;
+ boundaries.lower = multiply(boundaries.lower, cached_pow.f);
+ boundaries.upper = multiply(boundaries.upper, cached_pow.f);
+ assert(min_exp <= fp_value.e && fp_value.e <= -32);
+ --boundaries.lower; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
+ ++boundaries.upper; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
+ // Numbers outside of (lower, upper) definitely do not round to value.
+ grisu_shortest_handler handler{buf.data(), 0,
+ boundaries.upper - fp_value.f};
+ auto result =
+ grisu_gen_digits(fp(boundaries.upper, fp_value.e),
+ boundaries.upper - boundaries.lower, exp, handler);
+ if (result == digits::error) {
+ exp += handler.size - cached_exp10 - 1;
+ fallback_format(value, buf, exp);
+ return exp;
}
- if (result == digits::error) return false;
- buf.resize(to_unsigned(size));
+ buf.resize(to_unsigned(handler.size));
}
- exp -= cached_exp10;
- return true;
+ return exp - cached_exp10;
}
-template <typename Double>
-void sprintf_format(Double value, internal::buffer<char>& buf,
- core_format_specs spec) {
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+ buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
- FMT_ASSERT(buf.capacity() != 0, "empty buffer");
+ FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
+ static_assert(!std::is_same<T, float>(), "");
- // Build format string.
- enum { max_format_size = 10 }; // longest format: %#-*.*Lg
+ // Subtract 1 to account for the difference in precision since we use %e for
+ // both general and exponent format.
+ if (specs.format == float_format::general ||
+ specs.format == float_format::exp)
+ precision = (precision >= 0 ? precision : 6) - 1;
+
+ // Build the format string.
+ enum { max_format_size = 7 }; // Ths longest format is "%#.*Le".
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
- if (spec.has(HASH_FLAG) || !spec.type) *format_ptr++ = '#';
- if (spec.precision >= 0) {
+ if (specs.trailing_zeros) *format_ptr++ = '#';
+ if (precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
- if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
-
- char type = spec.type;
-
- if (type == '%')
- type = 'f';
- else if (type == 0)
- type = 'g';
-#if FMT_MSC_VER
- if (type == 'F') {
- // MSVC's printf doesn't support 'F'.
- type = 'f';
- }
-#endif
- *format_ptr++ = type;
+ if (std::is_same<T, long double>()) *format_ptr++ = 'L';
+ *format_ptr++ = specs.format != float_format::hex
+ ? (specs.format == float_format::fixed ? 'f' : 'e')
+ : (specs.upper ? 'A' : 'a');
*format_ptr = '\0';
// Format using snprintf.
- char* start = nullptr;
+ auto offset = buf.size();
for (;;) {
- std::size_t buffer_size = buf.capacity();
- start = &buf[0];
- int result =
- format_float(start, buffer_size, format, spec.precision, value);
- if (result >= 0) {
- unsigned n = internal::to_unsigned(result);
- if (n < buf.capacity()) {
- // Find the decimal point.
- auto p = buf.data(), end = p + n;
- if (*p == '+' || *p == '-') ++p;
- if (spec.type != 'a' && spec.type != 'A') {
- while (p < end && *p >= '0' && *p <= '9') ++p;
- if (p < end && *p != 'e' && *p != 'E') {
- if (*p != '.') *p = '.';
- if (!spec.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));
- n -= static_cast<unsigned>(p - where);
- }
- }
- }
- }
- buf.resize(n);
- break; // The buffer is large enough - continue with formatting.
+ auto begin = buf.data() + offset;
+ auto capacity = buf.capacity() - offset;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ if (precision > 100000)
+ throw std::runtime_error(
+ "fuzz mode - avoid large allocation inside snprintf");
+#endif
+ // Suppress the warning about a nonliteral format string.
+ auto snprintf_ptr = FMT_SNPRINTF;
+ int result = precision >= 0
+ ? snprintf_ptr(begin, capacity, format, precision, value)
+ : snprintf_ptr(begin, capacity, format, value);
+ if (result < 0) {
+ buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
+ continue;
+ }
+ unsigned 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'.
+ continue;
+ }
+ auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
+ if (specs.format == float_format::fixed) {
+ if (precision == 0) {
+ buf.resize(size);
+ return 0;
}
- buf.reserve(n + 1);
- } 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);
+ // Find and remove the decimal point.
+ auto end = begin + size, p = end;
+ do {
+ --p;
+ } while (is_digit(*p));
+ int fraction_size = static_cast<int>(end - p - 1);
+ std::memmove(p, p + 1, fraction_size);
+ buf.resize(size - 1);
+ return -fraction_size;
+ }
+ if (specs.format == float_format::hex) {
+ buf.resize(size + offset);
+ return 0;
+ }
+ // Find and parse the exponent.
+ auto end = begin + size, exp_pos = end;
+ do {
+ --exp_pos;
+ } while (*exp_pos != 'e');
+ char sign = exp_pos[1];
+ assert(sign == '+' || sign == '-');
+ int exp = 0;
+ auto p = exp_pos + 2; // Skip 'e' and sign.
+ do {
+ assert(is_digit(*p));
+ exp = exp * 10 + (*p++ - '0');
+ } while (p != end);
+ if (sign == '-') exp = -exp;
+ int fraction_size = 0;
+ if (exp_pos != begin + 1) {
+ // Remove trailing zeros.
+ auto fraction_end = exp_pos - 1;
+ while (*fraction_end == '0') --fraction_end;
+ // Move the fractional part left to get rid of the decimal point.
+ fraction_size = static_cast<int>(fraction_end - begin - 1);
+ std::memmove(begin + 1, begin + 2, fraction_size);
}
+ buf.resize(fraction_size + offset + 1);
+ return exp - fraction_size;
}
}
} // namespace internal
+template <> struct formatter<internal::bigint> {
+ format_parse_context::iterator parse(format_parse_context& ctx) {
+ return ctx.begin();
+ }
+
+ format_context::iterator format(const internal::bigint& n,
+ format_context& ctx) {
+ auto out = ctx.out();
+ bool first = true;
+ for (auto i = n.bigits_.size(); i > 0; --i) {
+ auto value = n.bigits_[i - 1];
+ if (first) {
+ out = format_to(out, "{:x}", value);
+ first = false;
+ continue;
+ }
+ out = format_to(out, "{:08x}", value);
+ }
+ if (n.exp_ > 0)
+ out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
+ return out;
+ }
+};
+
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
@@ -892,7 +1316,7 @@ FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
- writer w(out);
+ internal::writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
@@ -921,7 +1345,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
int result =
internal::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
- writer w(out);
+ internal::writer w(out);
w.write(message);
w.write(": ");
w.write(system_message);
@@ -959,20 +1383,10 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
-FMT_FUNC void vprint(std::FILE* f, wstring_view format_str, wformat_args args) {
- wmemory_buffer buffer;
- internal::vformat_to(buffer, format_str, args);
- internal::fwrite_fully(buffer.data(), sizeof(wchar_t), buffer.size(), f);
-}
-
FMT_FUNC void vprint(string_view format_str, format_args args) {
vprint(stdout, format_str, args);
}
-FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
- vprint(stdout, format_str, args);
-}
-
FMT_END_NAMESPACE
#ifdef _MSC_VER
diff --git a/src/third_party/fmt/dist/include/fmt/format.h b/src/third_party/fmt/dist/include/fmt/format.h
index a931bdcd04e..600b0eba454 100644
--- a/src/third_party/fmt/dist/include/fmt/format.h
+++ b/src/third_party/fmt/dist/include/fmt/format.h
@@ -2,44 +2,47 @@
Formatting library for C++
Copyright (c) 2012 - present, Victor Zverovich
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ --- Optional exception to the license ---
+
+ As an exception, if, as a result of your compiling your source code, portions
+ of this Software are embedded into a machine-executable object form of such
+ source code, you may redistribute such embedded portions in such object form
+ without including the above copyright and permission notices.
*/
#ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_
+#include "core.h"
+
#include <algorithm>
-#include <cassert>
+#include <cerrno>
#include <cmath>
#include <cstdint>
-#include <cstring>
-#include <iterator>
#include <limits>
#include <memory>
#include <stdexcept>
-#include "core.h"
-
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
@@ -66,6 +69,12 @@
# define FMT_HAS_BUILTIN(x) 0
#endif
+#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) >= 201603 && __cplusplus >= 201703
+# define FMT_FALLTHROUGH [[fallthrough]]
+#else
+# define FMT_FALLTHROUGH
+#endif
+
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# if FMT_MSC_VER
@@ -79,7 +88,7 @@ template <typename Exception> inline void do_throw(const Exception& x) {
}
} // namespace internal
FMT_END_NAMESPACE
-# define FMT_THROW(x) fmt::internal::do_throw(x)
+# define FMT_THROW(x) internal::do_throw(x)
# else
# define FMT_THROW(x) throw x
# endif
@@ -87,7 +96,7 @@ FMT_END_NAMESPACE
# define FMT_THROW(x) \
do { \
static_cast<void>(sizeof(x)); \
- assert(false); \
+ FMT_ASSERT(false, ""); \
} while (false)
# endif
#endif
@@ -118,14 +127,6 @@ FMT_END_NAMESPACE
# endif
#endif
-#ifdef FMT_USE_INT128
-// Do nothing.
-#elif defined(__SIZEOF_INT128__)
-# define FMT_USE_INT128 1
-#else
-# define FMT_USE_INT128 0
-#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
@@ -151,14 +152,14 @@ inline uint32_t clz(uint32_t x) {
unsigned long r = 0;
_BitScanReverse(&r, x);
- assert(x != 0);
+ FMT_ASSERT(x != 0, "");
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
# pragma warning(suppress : 6102)
return 31 - r;
}
-# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
+# define FMT_BUILTIN_CLZ(n) internal::clz(n)
# if defined(_WIN64) && !defined(__clang__)
# pragma intrinsic(_BitScanReverse64)
@@ -176,68 +177,270 @@ inline uint32_t clzll(uint64_t x) {
_BitScanReverse(&r, static_cast<uint32_t>(x));
# endif
- assert(x != 0);
+ FMT_ASSERT(x != 0, "");
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
# pragma warning(suppress : 6102)
return 63 - r;
}
-# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n)
+# define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
} // namespace internal
FMT_END_NAMESPACE
#endif
+// Enable the deprecated numeric alignment.
+#ifndef FMT_NUMERIC_ALIGN
+# define FMT_NUMERIC_ALIGN 1
+#endif
+
+// Enable the deprecated percent specifier.
+#ifndef FMT_DEPRECATED_PERCENT
+# define FMT_DEPRECATED_PERCENT 0
+#endif
+
FMT_BEGIN_NAMESPACE
namespace internal {
+// A helper function to suppress bogus "conditional expression is constant"
+// warnings.
+template <typename T> inline T const_check(T value) { return value; }
+
+// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
+// undefined behavior (e.g. due to type aliasing).
+// Example: uint64_t d = bit_cast<uint64_t>(2.718);
+template <typename Dest, typename Source>
+inline Dest bit_cast(const Source& source) {
+ static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
+ Dest dest;
+ std::memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+inline bool is_big_endian() {
+ auto u = 1u;
+ struct bytes {
+ char data[sizeof(u)];
+ };
+ return bit_cast<bytes>(u).data[0] == 0;
+}
+
// A fallback implementation of uintptr_t for systems that lack it.
struct fallback_uintptr {
unsigned char value[sizeof(void*)];
+
+ fallback_uintptr() = default;
+ explicit fallback_uintptr(const void* p) {
+ *this = bit_cast<fallback_uintptr>(p);
+ if (is_big_endian()) {
+ for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
+ std::swap(value[i], value[j]);
+ }
+ }
};
#ifdef UINTPTR_MAX
using uintptr_t = ::uintptr_t;
+inline uintptr_t to_uintptr(const void* p) { return bit_cast<uintptr_t>(p); }
#else
using uintptr_t = fallback_uintptr;
+inline fallback_uintptr to_uintptr(const void* p) {
+ return fallback_uintptr(p);
+}
#endif
-// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't produce
-// undefined behavior (e.g. due to type aliasing).
-// Example: uint64_t d = bit_cast<uint64_t>(2.718);
-template <typename Dest, typename Source>
-inline Dest bit_cast(const Source& source) {
- static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
- Dest dest;
- std::memcpy(&dest, &source, sizeof(dest));
- return dest;
+// Returns the largest possible value for type T. Same as
+// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr T max_value() {
+ return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr int num_bits() {
+ return std::numeric_limits<T>::digits;
+}
+template <> constexpr int num_bits<fallback_uintptr>() {
+ return static_cast<int>(sizeof(void*) *
+ std::numeric_limits<unsigned char>::digits);
}
-// An implementation of iterator_t for pre-C++20 systems.
+// An approximation of iterator_t for pre-C++20 systems.
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T&>()));
-#ifndef FMT_USE_GRISU
-# define FMT_USE_GRISU 1
+// Detect the iterator category of *any* given type in a SFINAE-friendly way.
+// Unfortunately, older implementations of std::iterator_traits are not safe
+// for use in a SFINAE-context.
+template <typename It, typename Enable = void>
+struct iterator_category : std::false_type {};
+
+template <typename T> struct iterator_category<T*> {
+ using type = std::random_access_iterator_tag;
+};
+
+template <typename It>
+struct iterator_category<It, void_t<typename It::iterator_category>> {
+ using type = typename It::iterator_category;
+};
+
+// Detect if *any* given type models the OutputIterator concept.
+template <typename It> class is_output_iterator {
+ // Check for mutability because all iterator categories derived from
+ // std::input_iterator_tag *may* also meet the requirements of an
+ // OutputIterator, thereby falling into the category of 'mutable iterators'
+ // [iterator.requirements.general] clause 4. The compiler reveals this
+ // property only at the point of *actually dereferencing* the iterator!
+ template <typename U>
+ static decltype(*(std::declval<U>())) test(std::input_iterator_tag);
+ template <typename U> static char& test(std::output_iterator_tag);
+ template <typename U> static const char& test(...);
+
+ using type = decltype(test<It>(typename iterator_category<It>::type{}));
+
+ public:
+ static const bool value = !std::is_const<remove_reference_t<type>>::value;
+};
+
+// A workaround for std::string not having mutable data() until C++17.
+template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
+ return &s[0];
+}
+template <typename Container>
+inline typename Container::value_type* get_data(Container& c) {
+ return c.data();
+}
+
+#ifdef _SECURE_SCL
+// Make a checked iterator to avoid MSVC warnings.
+template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
+template <typename T> checked_ptr<T> make_checked(T* p, std::size_t size) {
+ return {p, size};
+}
+#else
+template <typename T> using checked_ptr = T*;
+template <typename T> inline T* make_checked(T* p, std::size_t) { return p; }
#endif
-template <typename T> inline bool use_grisu() {
- return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559 &&
- sizeof(T) <= sizeof(double);
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline checked_ptr<typename Container::value_type> reserve(
+ std::back_insert_iterator<Container>& it, std::size_t n) {
+ Container& c = get_container(it);
+ std::size_t size = c.size();
+ c.resize(size + n);
+ return make_checked(get_data(c) + size, n);
}
-// A range with an iterator appending to a buffer.
-template <typename T> class buffer_range {
- public:
- using value_type = T;
- using iterator = std::back_insert_iterator<buffer<T>>;
+template <typename Iterator>
+inline Iterator& reserve(Iterator& it, std::size_t) {
+ return it;
+}
+// An output iterator that counts the number of objects written to it and
+// discards them.
+class counting_iterator {
private:
- iterator begin_;
+ std::size_t count_;
public:
- buffer_range(buffer<T>& buf) : begin_(std::back_inserter(buf)) {}
- explicit buffer_range(iterator it) : begin_(it) {}
- iterator begin() const { return begin_; }
+ using iterator_category = std::output_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using pointer = void;
+ using reference = void;
+ using _Unchecked_type = counting_iterator; // Mark iterator as checked.
+
+ struct value_type {
+ template <typename T> void operator=(const T&) {}
+ };
+
+ counting_iterator() : count_(0) {}
+
+ std::size_t count() const { return count_; }
+
+ counting_iterator& operator++() {
+ ++count_;
+ return *this;
+ }
+
+ counting_iterator operator++(int) {
+ auto it = *this;
+ ++*this;
+ return it;
+ }
+
+ value_type operator*() const { return {}; }
+};
+
+template <typename OutputIt> class truncating_iterator_base {
+ protected:
+ OutputIt out_;
+ std::size_t limit_;
+ std::size_t count_;
+
+ truncating_iterator_base(OutputIt out, std::size_t limit)
+ : out_(out), limit_(limit), count_(0) {}
+
+ public:
+ using iterator_category = std::output_iterator_tag;
+ using difference_type = void;
+ using pointer = void;
+ using reference = void;
+ using _Unchecked_type =
+ truncating_iterator_base; // Mark iterator as checked.
+
+ OutputIt base() const { return out_; }
+ std::size_t count() const { return count_; }
+};
+
+// An output iterator that truncates the output and counts the number of objects
+// written to it.
+template <typename OutputIt,
+ typename Enable = typename std::is_void<
+ typename std::iterator_traits<OutputIt>::value_type>::type>
+class truncating_iterator;
+
+template <typename OutputIt>
+class truncating_iterator<OutputIt, std::false_type>
+ : public truncating_iterator_base<OutputIt> {
+ using traits = std::iterator_traits<OutputIt>;
+
+ mutable typename traits::value_type blackhole_;
+
+ public:
+ using value_type = typename traits::value_type;
+
+ truncating_iterator(OutputIt out, std::size_t limit)
+ : truncating_iterator_base<OutputIt>(out, limit) {}
+
+ truncating_iterator& operator++() {
+ if (this->count_++ < this->limit_) ++this->out_;
+ return *this;
+ }
+
+ truncating_iterator operator++(int) {
+ auto it = *this;
+ ++*this;
+ return it;
+ }
+
+ value_type& operator*() const {
+ return this->count_ < this->limit_ ? *this->out_ : blackhole_;
+ }
+};
+
+template <typename OutputIt>
+class truncating_iterator<OutputIt, std::true_type>
+ : public truncating_iterator_base<OutputIt> {
+ public:
+ using value_type = typename OutputIt::container_type::value_type;
+
+ truncating_iterator(OutputIt out, std::size_t limit)
+ : truncating_iterator_base<OutputIt>(out, limit) {}
+
+ truncating_iterator& operator=(value_type val) {
+ if (this->count_++ < this->limit_) this->out_ = val;
+ return *this;
+ }
+
+ truncating_iterator& operator++() { return *this; }
+ truncating_iterator& operator++(int) { return *this; }
+ truncating_iterator& operator*() { return *this; }
};
// A range with the specified output iterator and value type.
@@ -249,22 +452,75 @@ class output_range {
public:
using value_type = T;
using iterator = OutputIt;
+ struct sentinel {};
explicit output_range(OutputIt it) : it_(it) {}
OutputIt begin() const { return it_; }
+ sentinel end() const { return {}; } // Sentinel is not used yet.
};
-#ifdef _SECURE_SCL
-// Make a checked iterator to avoid MSVC warnings.
-template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
-template <typename T> checked_ptr<T> make_checked(T* p, std::size_t size) {
- return {p, size};
+template <typename Char>
+inline size_t count_code_points(basic_string_view<Char> s) {
+ return s.size();
}
-#else
-template <typename T> using checked_ptr = T*;
-template <typename T> inline T* make_checked(T* p, std::size_t) { return p; }
+
+// Counts the number of code points in a UTF-8 string.
+inline size_t count_code_points(basic_string_view<char8_t> s) {
+ const char8_t* 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;
+ }
+ return num_code_points;
+}
+
+template <typename Char>
+inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
+ 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<char8_t> s, size_t n) {
+ const char8_t* 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) {
+ return i;
+ }
+ }
+ return s.size();
+}
+
+inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
+
+template <typename InputIt, typename OutChar>
+using needs_conversion = bool_constant<
+ std::is_same<typename std::iterator_traits<InputIt>::value_type,
+ char>::value &&
+ std::is_same<OutChar, char8_t>::value>;
+
+template <typename OutChar, typename InputIt, typename OutputIt,
+ FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
+OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
+ return std::copy(begin, end, it);
+}
+
+template <typename OutChar, typename InputIt, typename OutputIt,
+ FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
+OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
+ return std::transform(begin, end, it, to_char8_t);
+}
+
+#ifndef FMT_USE_GRISU
+# define FMT_USE_GRISU 1
#endif
+template <typename T> constexpr bool use_grisu() {
+ return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559 &&
+ sizeof(T) <= sizeof(double);
+}
+
template <typename T>
template <typename U>
void buffer<T>::append(const U* begin, const U* end) {
@@ -275,6 +531,17 @@ void buffer<T>::append(const U* begin, const U* end) {
}
} // namespace internal
+// A range with an iterator appending to a buffer.
+template <typename T>
+class buffer_range : public internal::output_range<
+ std::back_insert_iterator<internal::buffer<T>>, T> {
+ public:
+ using iterator = std::back_insert_iterator<internal::buffer<T>>;
+ using internal::output_range<iterator, T>::output_range;
+ buffer_range(internal::buffer<T>& buf)
+ : internal::output_range<iterator, T>(std::back_inserter(buf)) {}
+};
+
// A UTF-8 string view.
class u8string_view : public basic_string_view<char8_t> {
public:
@@ -302,7 +569,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 typedefs for common character types:
+ You can use one of the following type aliases for common character types:
+----------------+------------------------------+
| Type | Definition |
@@ -342,14 +609,14 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
void grow(std::size_t size) FMT_OVERRIDE;
public:
- typedef T value_type;
- typedef const T& const_reference;
+ using value_type = T;
+ using const_reference = const T&;
explicit basic_memory_buffer(const Allocator& alloc = Allocator())
: Allocator(alloc) {
this->set(store_, SIZE);
}
- ~basic_memory_buffer() { deallocate(); }
+ ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
private:
// Move data from other to this buffer.
@@ -378,15 +645,15 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
of the other object to it.
\endrst
*/
- basic_memory_buffer(basic_memory_buffer&& other) { move(other); }
+ basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
/**
\rst
Moves the content of the other ``basic_memory_buffer`` object to this one.
\endrst
*/
- basic_memory_buffer& operator=(basic_memory_buffer&& other) {
- assert(this != &other);
+ basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
+ FMT_ASSERT(this != &other, "");
deallocate();
move(other);
return *this;
@@ -398,6 +665,9 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
template <typename T, std::size_t SIZE, typename Allocator>
void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
+#endif
std::size_t old_capacity = this->capacity();
std::size_t new_capacity = old_capacity + old_capacity / 2;
if (size > new_capacity) new_capacity = size;
@@ -413,8 +683,8 @@ void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
}
-typedef basic_memory_buffer<char> memory_buffer;
-typedef basic_memory_buffer<wchar_t> wmemory_buffer;
+using memory_buffer = basic_memory_buffer<char>;
+using wmemory_buffer = basic_memory_buffer<wchar_t>;
/** A formatting error such as invalid format string. */
class FMT_API format_error : public std::runtime_error {
@@ -422,154 +692,17 @@ class FMT_API format_error : public std::runtime_error {
explicit format_error(const char* message) : std::runtime_error(message) {}
explicit format_error(const std::string& message)
: std::runtime_error(message) {}
- ~format_error() FMT_NOEXCEPT;
+ format_error(const format_error&) = default;
+ format_error& operator=(const format_error&) = default;
+ format_error(format_error&&) = default;
+ format_error& operator=(format_error&&) = default;
+ ~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
};
-template <typename Range> class basic_writer;
-using writer = basic_writer<internal::buffer_range<char>>;
-using wwriter = basic_writer<internal::buffer_range<wchar_t>>;
-
namespace internal {
-// A workaround for std::string not having mutable data() until C++17.
-template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
- return &s[0];
-}
-template <typename Container>
-inline typename Container::value_type* get_data(Container& c) {
- return c.data();
-}
-
-template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-inline checked_ptr<typename Container::value_type> reserve(
- std::back_insert_iterator<Container>& it, std::size_t n) {
- Container& c = internal::get_container(it);
- std::size_t size = c.size();
- c.resize(size + n);
- return make_checked(get_data(c) + size, n);
-}
-
-template <typename Iterator>
-inline Iterator& reserve(Iterator& it, std::size_t) {
- return it;
-}
-
-template <typename Char> class null_terminating_iterator;
-
-template <typename Char>
-FMT_CONSTEXPR_DECL const Char* pointer_from(null_terminating_iterator<Char> it);
-
-// An output iterator that counts the number of objects written to it and
-// discards them.
-template <typename T> class counting_iterator {
- private:
- std::size_t count_;
- mutable T blackhole_;
-
- public:
- typedef std::output_iterator_tag iterator_category;
- typedef T value_type;
- typedef std::ptrdiff_t difference_type;
- typedef T* pointer;
- typedef T& reference;
- typedef counting_iterator _Unchecked_type; // Mark iterator as checked.
-
- counting_iterator() : count_(0) {}
-
- std::size_t count() const { return count_; }
-
- counting_iterator& operator++() {
- ++count_;
- return *this;
- }
-
- counting_iterator operator++(int) {
- auto it = *this;
- ++*this;
- return it;
- }
-
- T& operator*() const { return blackhole_; }
-};
-
-template <typename OutputIt> class truncating_iterator_base {
- protected:
- OutputIt out_;
- std::size_t limit_;
- std::size_t count_;
-
- truncating_iterator_base(OutputIt out, std::size_t limit)
- : out_(out), limit_(limit), count_(0) {}
-
- public:
- typedef std::output_iterator_tag iterator_category;
- typedef void difference_type;
- typedef void pointer;
- typedef void reference;
- typedef truncating_iterator_base
- _Unchecked_type; // Mark iterator as checked.
-
- OutputIt base() const { return out_; }
- std::size_t count() const { return count_; }
-};
-
-// An output iterator that truncates the output and counts the number of objects
-// written to it.
-template <typename OutputIt,
- typename Enable = typename std::is_void<
- typename std::iterator_traits<OutputIt>::value_type>::type>
-class truncating_iterator;
-
-template <typename OutputIt>
-class truncating_iterator<OutputIt, std::false_type>
- : public truncating_iterator_base<OutputIt> {
- typedef std::iterator_traits<OutputIt> traits;
-
- mutable typename traits::value_type blackhole_;
-
- public:
- typedef typename traits::value_type value_type;
-
- truncating_iterator(OutputIt out, std::size_t limit)
- : truncating_iterator_base<OutputIt>(out, limit) {}
-
- truncating_iterator& operator++() {
- if (this->count_++ < this->limit_) ++this->out_;
- return *this;
- }
-
- truncating_iterator operator++(int) {
- auto it = *this;
- ++*this;
- return it;
- }
-
- value_type& operator*() const {
- return this->count_ < this->limit_ ? *this->out_ : blackhole_;
- }
-};
-
-template <typename OutputIt>
-class truncating_iterator<OutputIt, std::true_type>
- : public truncating_iterator_base<OutputIt> {
- public:
- typedef typename OutputIt::container_type::value_type value_type;
-
- truncating_iterator(OutputIt out, std::size_t limit)
- : truncating_iterator_base<OutputIt>(out, limit) {}
-
- truncating_iterator& operator=(value_type val) {
- if (this->count_++ < this->limit_) this->out_ = val;
- return *this;
- }
-
- truncating_iterator& operator++() { return *this; }
- truncating_iterator& operator++(int) { return *this; }
- truncating_iterator& operator*() { return *this; }
-};
-
// Returns true if value is negative, false otherwise.
-// Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
+// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
template <typename T, FMT_ENABLE_IF(std::numeric_limits<T>::is_signed)>
FMT_CONSTEXPR bool is_negative(T value) {
return value < 0;
@@ -579,32 +712,32 @@ FMT_CONSTEXPR bool is_negative(T) {
return false;
}
-template <typename T> struct int_traits {
- // Smallest of uint32_t and uint64_t that is large enough to represent
- // all values of T.
- using main_type =
- conditional_t<std::numeric_limits<T>::digits <= 32, uint32_t, uint64_t>;
-};
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of T.
+template <typename T>
+using uint32_or_64_or_128_t = conditional_t<
+ std::numeric_limits<T>::digits <= 32, uint32_t,
+ conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
-// Static data is placed in this class template to allow header-only
-// configuration.
+// Static data is placed in this class template for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
- static const uint64_t POWERS_OF_10_64[];
- static const uint32_t ZERO_OR_POWERS_OF_10_32[];
- static const uint64_t ZERO_OR_POWERS_OF_10_64[];
- static const uint64_t POW10_SIGNIFICANDS[];
- static const int16_t POW10_EXPONENTS[];
- static const char DIGITS[];
- static const char HEX_DIGITS[];
- static const char FOREGROUND_COLOR[];
- static const char BACKGROUND_COLOR[];
- static const char RESET_COLOR[5];
- static const wchar_t WRESET_COLOR[5];
+ static const uint64_t powers_of_10_64[];
+ static const uint32_t zero_or_powers_of_10_32[];
+ static const uint64_t zero_or_powers_of_10_64[];
+ static const uint64_t pow10_significands[];
+ static const int16_t pow10_exponents[];
+ static const char digits[];
+ static const char hex_digits[];
+ static const char foreground_color[];
+ static const char background_color[];
+ static const char reset_color[5];
+ static const wchar_t wreset_color[5];
+ static const char signs[];
};
FMT_EXTERN template struct basic_data<void>;
-// This is a struct rather than a typedef to avoid shadowing warnings in gcc.
+// This is a struct rather than an alias to avoid shadowing warnings in gcc.
struct data : basic_data<> {};
#ifdef FMT_BUILTIN_CLZLL
@@ -614,7 +747,7 @@ inline int count_digits(uint64_t n) {
// Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
// and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
- return t - (n < data::ZERO_OR_POWERS_OF_10_64[t]) + 1;
+ return t - (n < data::zero_or_powers_of_10_64[t]) + 1;
}
#else
// Fallback version of count_digits used when __builtin_clz is not available.
@@ -634,6 +767,23 @@ inline int count_digits(uint64_t n) {
}
#endif
+#if FMT_USE_INT128
+inline int count_digits(uint128_t n) {
+ int count = 1;
+ for (;;) {
+ // Integer division is slow so do it for a group of four digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ if (n < 10000) return count + 3;
+ n /= 10000U;
+ count += 4;
+ }
+}
+#endif
+
// Counts the number of digits in n. BITS = log2(radix).
template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
int num_digits = 0;
@@ -645,53 +795,14 @@ template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
template <> int count_digits<4>(internal::fallback_uintptr n);
-template <typename Char>
-inline size_t count_code_points(basic_string_view<Char> s) {
- return s.size();
-}
-
-// Counts the number of code points in a UTF-8 string.
-inline size_t count_code_points(basic_string_view<char8_t> s) {
- const char8_t* 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;
- }
- return num_code_points;
-}
-
-inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
-
-template <typename InputIt, typename OutChar>
-struct needs_conversion
- : bool_constant<
- std::is_same<typename std::iterator_traits<InputIt>::value_type,
- char>::value &&
- std::is_same<OutChar, char8_t>::value> {};
-
-template <typename OutChar, typename InputIt, typename OutputIt,
- FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
-OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
- return std::copy(begin, end, it);
-}
-
-template <typename OutChar, typename InputIt, typename OutputIt,
- FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
-OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
- return std::transform(begin, end, it, to_char8_t);
-}
-
-#if FMT_HAS_CPP_ATTRIBUTE(always_inline)
-# define FMT_ALWAYS_INLINE __attribute__((always_inline))
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE
#endif
-template <typename Handler>
-inline char* lg(uint32_t n, Handler h) FMT_ALWAYS_INLINE;
-
// Computes g = floor(log10(n)) and calls h.on<g>(n);
-template <typename Handler> inline char* lg(uint32_t n, Handler h) {
+template <typename Handler> FMT_ALWAYS_INLINE char* lg(uint32_t n, Handler h) {
return n < 100 ? n < 10 ? h.template on<0>(n) : h.template on<1>(n)
: n < 1000000
? n < 10000 ? n < 1000 ? h.template on<2>(n)
@@ -711,7 +822,7 @@ class decimal_formatter {
char* buffer_;
void write_pair(unsigned N, uint32_t index) {
- std::memcpy(buffer_ + N, data::DIGITS + index * 2, 2);
+ std::memcpy(buffer_ + N, data::digits + index * 2, 2);
}
public:
@@ -728,7 +839,7 @@ class decimal_formatter {
unsigned n = N - 1;
unsigned a = n / 5 * n * 53 / 16;
uint64_t t =
- ((1ULL << (32 + a)) / data::ZERO_OR_POWERS_OF_10_32[n] + 1 - n / 9);
+ ((1ULL << (32 + a)) / data::zero_or_powers_of_10_32[n] + 1 - n / 9);
t = ((t * u) >> a) + n / 5 * 4;
write_pair(0, t >> 32);
for (unsigned i = 2; i < N; i += 2) {
@@ -744,75 +855,44 @@ class decimal_formatter {
}
};
-// An lg handler that formats a decimal number with a terminating null.
-class decimal_formatter_null : public decimal_formatter {
- public:
- explicit decimal_formatter_null(char* buf) : decimal_formatter(buf) {}
-
- template <unsigned N> char* on(uint32_t u) {
- char* buf = decimal_formatter::on<N>(u);
- *buf = '\0';
- return buf;
- }
-};
-
#ifdef FMT_BUILTIN_CLZ
// Optional version of count_digits for better performance on 32-bit platforms.
inline int count_digits(uint32_t n) {
int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
- return t - (n < data::ZERO_OR_POWERS_OF_10_32[t]) + 1;
+ return t - (n < data::zero_or_powers_of_10_32[t]) + 1;
}
#endif
-// A functor that doesn't add a thousands separator.
-struct no_thousands_sep {
- typedef char char_type;
-
- template <typename Char> void operator()(Char*) {}
-
- enum { size = 0 };
-};
-
-// A functor that adds a thousands separator.
-template <typename Char> class add_thousands_sep {
- private:
- basic_string_view<Char> sep_;
-
- // Index of a decimal digit with the least significant digit having index 0.
- unsigned digit_index_;
-
- public:
- typedef Char char_type;
-
- explicit add_thousands_sep(basic_string_view<Char> sep)
- : sep_(sep), digit_index_(0) {}
-
- void operator()(Char*& buffer) {
- if (++digit_index_ % 3 != 0) return;
- buffer -= sep_.size();
- std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(),
- internal::make_checked(buffer, sep_.size()));
- }
-
- enum { size = 1 };
-};
+template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
+template <typename Char> inline std::string grouping(locale_ref loc) {
+ return grouping_impl<char>(loc);
+}
+template <> inline std::string grouping<wchar_t>(locale_ref loc) {
+ return grouping_impl<wchar_t>(loc);
+}
template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
-
template <typename Char> inline Char thousands_sep(locale_ref loc) {
return Char(thousands_sep_impl<char>(loc));
}
-
template <> inline wchar_t thousands_sep(locale_ref loc) {
return thousands_sep_impl<wchar_t>(loc);
}
+template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc);
+template <typename Char> inline Char decimal_point(locale_ref loc) {
+ return Char(decimal_point_impl<char>(loc));
+}
+template <> inline wchar_t decimal_point(locale_ref loc) {
+ return decimal_point_impl<wchar_t>(loc);
+}
+
// Formats a decimal unsigned integer value writing into buffer.
-// thousands_sep is a functor that is called after writing each char to
-// add a thousands separator if necessary.
-template <typename UInt, typename Char, typename ThousandsSep>
+// add_thousands_sep is called after writing each char to add a thousands
+// separator if necessary.
+template <typename UInt, typename Char, typename F>
inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
- ThousandsSep thousands_sep) {
+ F add_thousands_sep) {
FMT_ASSERT(num_digits >= 0, "invalid digit count");
buffer += num_digits;
Char* end = buffer;
@@ -820,41 +900,44 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
- unsigned index = static_cast<unsigned>((value % 100) * 2);
+ auto index = static_cast<unsigned>((value % 100) * 2);
value /= 100;
- *--buffer = static_cast<Char>(data::DIGITS[index + 1]);
- thousands_sep(buffer);
- *--buffer = static_cast<Char>(data::DIGITS[index]);
- thousands_sep(buffer);
+ *--buffer = static_cast<Char>(data::digits[index + 1]);
+ add_thousands_sep(buffer);
+ *--buffer = static_cast<Char>(data::digits[index]);
+ add_thousands_sep(buffer);
}
if (value < 10) {
*--buffer = static_cast<Char>('0' + value);
return end;
}
- unsigned index = static_cast<unsigned>(value * 2);
- *--buffer = static_cast<Char>(data::DIGITS[index + 1]);
- thousands_sep(buffer);
- *--buffer = static_cast<Char>(data::DIGITS[index]);
+ auto index = static_cast<unsigned>(value * 2);
+ *--buffer = static_cast<Char>(data::digits[index + 1]);
+ add_thousands_sep(buffer);
+ *--buffer = static_cast<Char>(data::digits[index]);
return end;
}
-template <typename OutChar, typename UInt, typename Iterator,
- typename ThousandsSep>
+template <typename Int> constexpr int digits10() noexcept {
+ return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr int digits10<int128_t>() noexcept { return 38; }
+template <> constexpr int digits10<uint128_t>() noexcept { return 38; }
+
+template <typename Char, typename UInt, typename Iterator, typename F>
inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
- ThousandsSep sep) {
+ F add_thousands_sep) {
FMT_ASSERT(num_digits >= 0, "invalid digit count");
- typedef typename ThousandsSep::char_type char_type;
// Buffer should be large enough to hold all digits (<= digits10 + 1).
- enum { max_size = std::numeric_limits<UInt>::digits10 + 1 };
- FMT_ASSERT(ThousandsSep::size <= 1, "invalid separator");
- char_type buffer[max_size + max_size / 3];
- auto end = format_decimal(buffer, value, num_digits, sep);
- return internal::copy_str<OutChar>(buffer, end, out);
+ enum { max_size = digits10<UInt>() + 1 };
+ Char buffer[2 * max_size];
+ auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
+ return internal::copy_str<Char>(buffer, end, out);
}
-template <typename OutChar, typename It, typename UInt>
+template <typename Char, typename It, typename UInt>
inline It format_decimal(It out, UInt value, int num_digits) {
- return format_decimal<OutChar>(out, value, num_digits, no_thousands_sep());
+ return format_decimal<Char>(out, value, num_digits, [](Char*) {});
}
template <unsigned BASE_BITS, typename Char, typename UInt>
@@ -863,7 +946,7 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits,
buffer += num_digits;
Char* end = buffer;
do {
- const char* digits = upper ? "0123456789ABCDEF" : data::HEX_DIGITS;
+ const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits;
unsigned digit = (value & ((1 << BASE_BITS) - 1));
*--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
: digits[digit]);
@@ -886,7 +969,7 @@ Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
auto p = buffer;
for (int i = 0; i < char_digits; ++i) {
unsigned digit = (value & ((1 << BASE_BITS) - 1));
- *--p = static_cast<Char>(data::HEX_DIGITS[digit]);
+ *--p = static_cast<Char>(data::hex_digits[digit]);
value >>= BASE_BITS;
}
}
@@ -895,9 +978,8 @@ Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
- // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1)
- // and null.
- char buffer[std::numeric_limits<UInt>::digits / BASE_BITS + 2];
+ // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
+ char buffer[num_bits<UInt>() / BASE_BITS + 1];
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
return internal::copy_str<Char>(buffer, buffer + num_digits, out);
}
@@ -945,82 +1027,90 @@ class utf16_to_utf8 {
FMT_API int convert(wstring_view s);
};
-FMT_API void format_windows_error(fmt::internal::buffer<char>& out,
- int error_code,
- fmt::string_view message) FMT_NOEXCEPT;
+FMT_API void format_windows_error(internal::buffer<char>& out, int error_code,
+ string_view message) FMT_NOEXCEPT;
#endif
template <typename T = void> struct null {};
-} // namespace internal
-enum alignment {
- ALIGN_DEFAULT,
- ALIGN_LEFT,
- ALIGN_RIGHT,
- ALIGN_CENTER,
- ALIGN_NUMERIC
-};
+// Workaround an array initialization issue in gcc 4.8.
+template <typename Char> struct fill_t {
+ private:
+ Char data_[6];
+
+ public:
+ FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
+ FMT_CONSTEXPR const Char& operator[](size_t index) const {
+ return data_[index];
+ }
-// Flags.
-enum { SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8 };
-
-// An alignment specifier.
-struct align_spec {
- unsigned width_;
- // Fill is always wchar_t and cast to char if necessary to avoid having
- // two specialization of AlignSpec and its subclasses.
- wchar_t fill_;
- alignment align_;
-
- FMT_CONSTEXPR align_spec() : width_(0), fill_(' '), align_(ALIGN_DEFAULT) {}
- FMT_CONSTEXPR unsigned width() const { return width_; }
- FMT_CONSTEXPR wchar_t fill() const { return fill_; }
- FMT_CONSTEXPR alignment align() const { return align_; }
+ static FMT_CONSTEXPR fill_t<Char> make() {
+ auto fill = fill_t<Char>();
+ fill[0] = Char(' ');
+ return fill;
+ }
};
+} // namespace internal
+
+// We cannot use enum classes as bit fields because of a gcc bug
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
+namespace align {
+enum type { none, left, right, center, numeric };
+}
+using align_t = align::type;
-struct core_format_specs {
+namespace sign {
+enum type { none, minus, plus, space };
+}
+using sign_t = sign::type;
+
+// Format specifiers for built-in and string types.
+template <typename Char> struct basic_format_specs {
+ int width;
int precision;
- uint_least8_t flags;
char type;
+ align_t align : 4;
+ sign_t sign : 3;
+ bool alt : 1; // Alternate form ('#').
+ internal::fill_t<Char> fill;
- FMT_CONSTEXPR core_format_specs() : precision(-1), flags(0), type(0) {}
- FMT_CONSTEXPR bool has(unsigned f) const { return (flags & f) != 0; }
- FMT_CONSTEXPR bool has_precision() const { return precision != -1; }
-};
-
-// Format specifiers.
-template <typename Char>
-struct basic_format_specs : align_spec, core_format_specs {
- FMT_CONSTEXPR basic_format_specs() {}
+ constexpr basic_format_specs()
+ : width(0),
+ precision(-1),
+ type(0),
+ align(align::none),
+ sign(sign::none),
+ alt(false),
+ fill(internal::fill_t<Char>::make()) {}
};
-typedef basic_format_specs<char> format_specs;
+using format_specs = basic_format_specs<char>;
namespace internal {
-namespace grisu_options {
-enum { fixed = 1, grisu3 = 2 };
-}
-
-// Formats value using the Grisu algorithm:
-// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
-template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
-FMT_API bool grisu_format(Double, buffer<char>&, int, unsigned, int&);
-template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
-inline bool grisu_format(Double, buffer<char>&, int, unsigned, int&) {
- return false;
-}
+// A floating-point presentation format.
+enum class float_format : unsigned char {
+ general, // General: exponent notation or fixed point based on magnitude.
+ exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+ fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
+ hex
+};
-struct gen_digits_params {
- int num_digits;
- bool fixed;
- bool upper;
- bool trailing_zeros;
+struct float_specs {
+ int precision;
+ float_format format : 8;
+ sign_t sign : 8;
+ bool upper : 1;
+ bool locale : 1;
+ bool percent : 1;
+ bool binary32 : 1;
+ bool use_grisu : 1;
+ bool trailing_zeros : 1;
};
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
template <typename Char, typename It> It write_exponent(int exp, It it) {
- FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
+ FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
if (exp < 0) {
*it++ = static_cast<Char>('-');
exp = -exp;
@@ -1028,78 +1118,133 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
*it++ = static_cast<Char>('+');
}
if (exp >= 100) {
- *it++ = static_cast<Char>(static_cast<char>('0' + exp / 100));
+ const char* top = data::digits + (exp / 100) * 2;
+ if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+ *it++ = static_cast<Char>(top[1]);
exp %= 100;
- const char* d = data::DIGITS + exp * 2;
- *it++ = static_cast<Char>(d[0]);
- *it++ = static_cast<Char>(d[1]);
- } else {
- const char* d = data::DIGITS + exp * 2;
- *it++ = static_cast<Char>(d[0]);
- *it++ = static_cast<Char>(d[1]);
}
+ const char* d = data::digits + exp * 2;
+ *it++ = static_cast<Char>(d[0]);
+ *it++ = static_cast<Char>(d[1]);
return it;
}
-// The number is given as v = digits * pow(10, exp).
-template <typename Char, typename It>
-It grisu_prettify(const char* digits, int size, int exp, It it,
- gen_digits_params params) {
- // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
- int full_exp = size + exp;
- if (!params.fixed) {
- // Insert a decimal point after the first digit and add an exponent.
- *it++ = static_cast<Char>(*digits);
- if (size > 1) *it++ = static_cast<Char>('.');
- exp += size - 1;
- it = copy_str<Char>(digits + 1, digits + size, it);
- if (size < params.num_digits)
- it = std::fill_n(it, params.num_digits - size, static_cast<Char>('0'));
- *it++ = static_cast<Char>(params.upper ? 'E' : 'e');
- return write_exponent<Char>(exp, it);
- }
- if (size <= full_exp) {
- // 1234e7 -> 12340000000[.0+]
- it = copy_str<Char>(digits, digits + size, it);
- it = std::fill_n(it, full_exp - size, static_cast<Char>('0'));
- int num_zeros = (std::max)(params.num_digits - full_exp, 1);
- if (params.trailing_zeros) {
- *it++ = static_cast<Char>('.');
- it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+template <typename Char> class float_writer {
+ private:
+ // The number is given as v = digits_ * pow(10, exp_).
+ const char* digits_;
+ int num_digits_;
+ int exp_;
+ size_t size_;
+ float_specs specs_;
+ Char decimal_point_;
+
+ template <typename It> It prettify(It it) const {
+ // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
+ int full_exp = num_digits_ + exp_;
+ if (specs_.format == float_format::exp) {
+ // Insert a decimal point after the first digit and add an exponent.
+ *it++ = static_cast<Char>(*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_;
+ it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
+ if (trailing_zeros)
+ it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+ *it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
+ return write_exponent<Char>(full_exp - 1, it);
}
- } else if (full_exp > 0) {
- // 1234e-2 -> 12.34[0+]
- it = copy_str<Char>(digits, digits + full_exp, it);
- if (!params.trailing_zeros) {
- // Remove trailing zeros.
- while (size > full_exp && digits[size - 1] == '0') --size;
- if (size != full_exp) *it++ = static_cast<Char>('.');
- return copy_str<Char>(digits + full_exp, digits + size, it);
+ if (num_digits_ <= full_exp) {
+ // 1234e7 -> 12340000000[.0+]
+ it = copy_str<Char>(digits_, digits_ + num_digits_, it);
+ it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('0'));
+ if (specs_.trailing_zeros) {
+ *it++ = decimal_point_;
+ int num_zeros = specs_.precision - full_exp;
+ if (num_zeros <= 0) {
+ if (specs_.format != float_format::fixed)
+ *it++ = static_cast<Char>('0');
+ return it;
+ }
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ if (num_zeros > 1000)
+ throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
+#endif
+ it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+ }
+ } else if (full_exp > 0) {
+ // 1234e-2 -> 12.34[0+]
+ it = copy_str<Char>(digits_, digits_ + full_exp, it);
+ if (!specs_.trailing_zeros) {
+ // Remove trailing zeros.
+ int num_digits = num_digits_;
+ while (num_digits > full_exp && digits_[num_digits - 1] == '0')
+ --num_digits;
+ if (num_digits != full_exp) *it++ = decimal_point_;
+ return copy_str<Char>(digits_ + full_exp, digits_ + num_digits, it);
+ }
+ *it++ = decimal_point_;
+ it = copy_str<Char>(digits_ + full_exp, digits_ + num_digits_, it);
+ if (specs_.precision > num_digits_) {
+ // Add trailing zeros.
+ int num_zeros = specs_.precision - num_digits_;
+ it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+ }
+ } else {
+ // 1234e-6 -> 0.001234
+ *it++ = static_cast<Char>('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)
+ while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits;
+ if (num_zeros != 0 || num_digits != 0) {
+ *it++ = decimal_point_;
+ it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+ it = copy_str<Char>(digits_, digits_ + num_digits, it);
+ }
}
- *it++ = static_cast<Char>('.');
- it = copy_str<Char>(digits + full_exp, digits + size, it);
- if (params.num_digits > size) {
- // Add trailing zeros.
- int num_zeros = params.num_digits - size;
- it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+ return it;
+ }
+
+ public:
+ float_writer(const char* digits, int num_digits, int exp, float_specs specs,
+ Char decimal_point)
+ : digits_(digits),
+ num_digits_(num_digits),
+ exp_(exp),
+ specs_(specs),
+ decimal_point_(decimal_point) {
+ int full_exp = num_digits + exp - 1;
+ int precision = specs.precision > 0 ? specs.precision : 16;
+ if (specs_.format == float_format::general &&
+ !(full_exp >= -4 && full_exp < precision)) {
+ specs_.format = float_format::exp;
}
- } else {
- // 1234e-6 -> 0.001234
- *it++ = static_cast<Char>('0');
- *it++ = static_cast<Char>('.');
- int num_zeros = -full_exp;
- if (params.num_digits >= 0 && params.num_digits < num_zeros)
- num_zeros = params.num_digits;
- it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
- if (!params.trailing_zeros)
- while (size > 0 && digits[size - 1] == '0') --size;
- it = copy_str<Char>(digits, digits + size, it);
+ size_ = prettify(counting_iterator()).count();
+ size_ += specs.sign ? 1 : 0;
}
- return it;
-}
-template <typename Double>
-void sprintf_format(Double, internal::buffer<char>&, core_format_specs);
+ size_t size() const { return size_; }
+ size_t width() const { return size(); }
+
+ template <typename It> void operator()(It&& it) {
+ if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
+ it = prettify(it);
+ }
+};
+
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
+
+// Formats a floating-point number with snprintf.
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+ buffer<char>& buf);
+
+template <typename T> T promote_float(T value) { return value; }
+inline double promote_float(float value) { return value; }
template <typename Handler>
FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
@@ -1127,33 +1272,56 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
}
}
-template <typename Handler>
-FMT_CONSTEXPR void handle_float_type_spec(char spec, Handler&& handler) {
- switch (spec) {
+template <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR float_specs parse_float_type_spec(
+ const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
+ auto result = float_specs();
+ result.trailing_zeros = specs.alt;
+ switch (specs.type) {
case 0:
- case 'g':
+ result.format = float_format::general;
+ result.trailing_zeros |= specs.precision != 0;
+ break;
case 'G':
- handler.on_general();
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'g':
+ result.format = float_format::general;
break;
- case 'e':
case 'E':
- handler.on_exp();
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'e':
+ result.format = float_format::exp;
+ result.trailing_zeros |= specs.precision != 0;
break;
- case 'f':
case 'F':
- handler.on_fixed();
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'f':
+ result.format = float_format::fixed;
+ result.trailing_zeros |= specs.precision != 0;
break;
+#if FMT_DEPRECATED_PERCENT
case '%':
- handler.on_percent();
+ result.format = float_format::fixed;
+ result.percent = true;
break;
- case 'a':
+#endif
case 'A':
- handler.on_hex();
+ result.upper = true;
+ FMT_FALLTHROUGH;
+ case 'a':
+ result.format = float_format::hex;
+ break;
+ case 'n':
+ result.locale = true;
break;
default:
- handler.on_error();
+ eh.on_error("invalid type specifier");
break;
}
+ return result;
}
template <typename Char, typename Handler>
@@ -1161,7 +1329,7 @@ FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* specs,
Handler&& handler) {
if (!specs) return handler.on_char();
if (specs->type && specs->type != 'c') return handler.on_int();
- if (specs->align() == ALIGN_NUMERIC || specs->flags != 0)
+ if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
handler.on_error("invalid format specifier for char");
handler.on_char();
}
@@ -1202,23 +1370,6 @@ template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
};
template <typename ErrorHandler>
-class float_type_checker : private ErrorHandler {
- public:
- FMT_CONSTEXPR explicit float_type_checker(ErrorHandler eh)
- : ErrorHandler(eh) {}
-
- FMT_CONSTEXPR void on_general() {}
- FMT_CONSTEXPR void on_exp() {}
- FMT_CONSTEXPR void on_fixed() {}
- FMT_CONSTEXPR void on_percent() {}
- FMT_CONSTEXPR void on_hex() {}
-
- FMT_CONSTEXPR void on_error() {
- ErrorHandler::on_error("invalid type specifier");
- }
-};
-
-template <typename ErrorHandler>
class char_specs_checker : public ErrorHandler {
private:
char type_;
@@ -1246,30 +1397,446 @@ class cstring_type_checker : public ErrorHandler {
template <typename Context>
void arg_map<Context>::init(const basic_format_args<Context>& args) {
if (map_) return;
- map_ = new entry[args.max_size()];
+ map_ = new entry[internal::to_unsigned(args.max_size())];
if (args.is_packed()) {
- for (unsigned i = 0; /*nothing*/; ++i) {
+ 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]);
}
}
- for (unsigned i = 0;; ++i) {
+ for (int i = 0, n = args.max_size(); i < n; ++i) {
auto type = args.args_[i].type_;
- if (type == internal::none_type) return;
if (type == internal::named_arg_type) push_back(args.args_[i].value_);
}
}
+template <typename Char> struct nonfinite_writer {
+ sign_t sign;
+ const char* str;
+ static constexpr size_t str_size = 3;
+
+ size_t size() const { return str_size + (sign ? 1 : 0); }
+ size_t width() const { return size(); }
+
+ template <typename It> void operator()(It&& it) const {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ it = copy_str<Char>(str, str + str_size, it);
+ }
+};
+
+// This template provides operations for formatting and writing data into a
+// character range.
+template <typename Range> class basic_writer {
+ public:
+ using char_type = typename Range::value_type;
+ using iterator = typename Range::iterator;
+ using format_specs = basic_format_specs<char_type>;
+
+ private:
+ iterator out_; // Output iterator.
+ locale_ref locale_;
+
+ // Attempts to reserve space for n extra characters in the output range.
+ // Returns a pointer to the reserved range or a reference to out_.
+ auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
+ return internal::reserve(out_, n);
+ }
+
+ template <typename F> struct padded_int_writer {
+ size_t size_;
+ string_view prefix;
+ char_type fill;
+ std::size_t padding;
+ F f;
+
+ size_t size() const { return size_; }
+ size_t width() const { return size_; }
+
+ template <typename It> void operator()(It&& it) const {
+ if (prefix.size() != 0)
+ it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
+ it = std::fill_n(it, padding, fill);
+ f(it);
+ }
+ };
+
+ // Writes an integer in the format
+ // <left-padding><prefix><numeric-padding><digits><right-padding>
+ // where <digits> are written by f(it).
+ template <typename F>
+ void write_int(int num_digits, string_view prefix, format_specs specs, F f) {
+ std::size_t size = prefix.size() + to_unsigned(num_digits);
+ char_type fill = specs.fill[0];
+ std::size_t padding = 0;
+ if (specs.align == align::numeric) {
+ auto unsiged_width = to_unsigned(specs.width);
+ if (unsiged_width > size) {
+ padding = unsiged_width - size;
+ size = unsiged_width;
+ }
+ } else if (specs.precision > num_digits) {
+ size = prefix.size() + to_unsigned(specs.precision);
+ padding = to_unsigned(specs.precision - num_digits);
+ fill = static_cast<char_type>('0');
+ }
+ if (specs.align == align::none) specs.align = align::right;
+ write_padded(specs, padded_int_writer<F>{size, prefix, fill, padding, f});
+ }
+
+ // Writes a decimal integer.
+ template <typename Int> void write_decimal(Int value) {
+ auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
+ bool negative = is_negative(value);
+ // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+ if (negative) abs_value = ~abs_value + 1;
+ int num_digits = count_digits(abs_value);
+ auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
+ if (negative) *it++ = static_cast<char_type>('-');
+ it = format_decimal<char_type>(it, abs_value, num_digits);
+ }
+
+ // The handle_int_type_spec handler that writes an integer.
+ template <typename Int, typename Specs> struct int_writer {
+ using unsigned_type = uint32_or_64_or_128_t<Int>;
+
+ basic_writer<Range>& writer;
+ const Specs& specs;
+ unsigned_type abs_value;
+ char prefix[4];
+ unsigned prefix_size;
+
+ string_view get_prefix() const { return string_view(prefix, prefix_size); }
+
+ int_writer(basic_writer<Range>& w, Int value, const Specs& s)
+ : writer(w),
+ specs(s),
+ abs_value(static_cast<unsigned_type>(value)),
+ prefix_size(0) {
+ if (is_negative(value)) {
+ prefix[0] = '-';
+ ++prefix_size;
+ abs_value = 0 - abs_value;
+ } else if (specs.sign != sign::none && specs.sign != sign::minus) {
+ prefix[0] = specs.sign == sign::plus ? '+' : ' ';
+ ++prefix_size;
+ }
+ }
+
+ struct dec_writer {
+ unsigned_type abs_value;
+ int num_digits;
+
+ template <typename It> void operator()(It&& it) const {
+ it = internal::format_decimal<char_type>(it, abs_value, num_digits);
+ }
+ };
+
+ void on_dec() {
+ int num_digits = count_digits(abs_value);
+ writer.write_int(num_digits, get_prefix(), specs,
+ dec_writer{abs_value, num_digits});
+ }
+
+ struct hex_writer {
+ int_writer& self;
+ int num_digits;
+
+ template <typename It> void operator()(It&& it) const {
+ it = format_uint<4, char_type>(it, self.abs_value, num_digits,
+ self.specs.type != 'x');
+ }
+ };
+
+ void on_hex() {
+ if (specs.alt) {
+ prefix[prefix_size++] = '0';
+ prefix[prefix_size++] = specs.type;
+ }
+ int num_digits = count_digits<4>(abs_value);
+ writer.write_int(num_digits, get_prefix(), specs,
+ hex_writer{*this, num_digits});
+ }
+
+ template <int BITS> struct bin_writer {
+ unsigned_type abs_value;
+ int num_digits;
+
+ template <typename It> void operator()(It&& it) const {
+ it = format_uint<BITS, char_type>(it, abs_value, num_digits);
+ }
+ };
+
+ void on_bin() {
+ if (specs.alt) {
+ prefix[prefix_size++] = '0';
+ prefix[prefix_size++] = static_cast<char>(specs.type);
+ }
+ int num_digits = count_digits<1>(abs_value);
+ writer.write_int(num_digits, get_prefix(), specs,
+ bin_writer<1>{abs_value, num_digits});
+ }
+
+ void on_oct() {
+ int num_digits = count_digits<3>(abs_value);
+ if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
+ // Octal prefix '0' is counted as a digit, so only add it if precision
+ // is not greater than the number of digits.
+ prefix[prefix_size++] = '0';
+ }
+ writer.write_int(num_digits, get_prefix(), specs,
+ bin_writer<3>{abs_value, num_digits});
+ }
+
+ enum { sep_size = 1 };
+
+ struct num_writer {
+ unsigned_type abs_value;
+ int size;
+ const std::string& groups;
+ char_type sep;
+
+ template <typename It> void operator()(It&& it) const {
+ basic_string_view<char_type> s(&sep, sep_size);
+ // Index of a decimal digit with the least significant digit having
+ // index 0.
+ int digit_index = 0;
+ std::string::const_iterator group = groups.cbegin();
+ it = format_decimal<char_type>(
+ it, abs_value, size,
+ [this, s, &group, &digit_index](char_type*& buffer) {
+ if (*group <= 0 || ++digit_index % *group != 0 ||
+ *group == max_value<char>())
+ return;
+ if (group + 1 != groups.cend()) {
+ digit_index = 0;
+ ++group;
+ }
+ buffer -= s.size();
+ std::uninitialized_copy(s.data(), s.data() + s.size(),
+ make_checked(buffer, s.size()));
+ });
+ }
+ };
+
+ void on_num() {
+ std::string groups = grouping<char_type>(writer.locale_);
+ if (groups.empty()) return on_dec();
+ auto sep = thousands_sep<char_type>(writer.locale_);
+ if (!sep) return on_dec();
+ int num_digits = count_digits(abs_value);
+ int size = num_digits;
+ std::string::const_iterator group = groups.cbegin();
+ while (group != groups.cend() && num_digits > *group && *group > 0 &&
+ *group != max_value<char>()) {
+ size += sep_size;
+ num_digits -= *group;
+ ++group;
+ }
+ if (group == groups.cend())
+ size += sep_size * ((num_digits - 1) / groups.back());
+ writer.write_int(size, get_prefix(), specs,
+ num_writer{abs_value, size, groups, sep});
+ }
+
+ FMT_NORETURN void on_error() {
+ FMT_THROW(format_error("invalid type specifier"));
+ }
+ };
+
+ template <typename Char> struct str_writer {
+ const Char* s;
+ size_t size_;
+
+ size_t size() const { return size_; }
+ size_t width() const {
+ return count_code_points(basic_string_view<Char>(s, size_));
+ }
+
+ template <typename It> void operator()(It&& it) const {
+ it = copy_str<char_type>(s, s + size_, it);
+ }
+ };
+
+ template <typename UIntPtr> struct pointer_writer {
+ UIntPtr value;
+ int num_digits;
+
+ size_t size() const { return to_unsigned(num_digits) + 2; }
+ size_t width() const { return size(); }
+
+ template <typename It> void operator()(It&& it) const {
+ *it++ = static_cast<char_type>('0');
+ *it++ = static_cast<char_type>('x');
+ it = format_uint<4, char_type>(it, value, num_digits);
+ }
+ };
+
+ public:
+ explicit basic_writer(Range out, locale_ref loc = locale_ref())
+ : out_(out.begin()), locale_(loc) {}
+
+ iterator out() const { return out_; }
+
+ // Writes a value in the format
+ // <left-padding><value><right-padding>
+ // where <value> is written by f(it).
+ template <typename F> void write_padded(const format_specs& specs, F&& f) {
+ // User-perceived width (in code points).
+ unsigned width = to_unsigned(specs.width);
+ 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;
+ if (specs.align == align::right) {
+ it = std::fill_n(it, padding, fill);
+ f(it);
+ } else if (specs.align == align::center) {
+ std::size_t left_padding = padding / 2;
+ it = std::fill_n(it, left_padding, fill);
+ f(it);
+ it = std::fill_n(it, padding - left_padding, fill);
+ } else {
+ f(it);
+ it = std::fill_n(it, padding, fill);
+ }
+ }
+
+ void write(int value) { write_decimal(value); }
+ void write(long value) { write_decimal(value); }
+ void write(long long value) { write_decimal(value); }
+
+ void write(unsigned value) { write_decimal(value); }
+ void write(unsigned long value) { write_decimal(value); }
+ void write(unsigned long long value) { write_decimal(value); }
+
+#if FMT_USE_INT128
+ void write(int128_t value) { write_decimal(value); }
+ void write(uint128_t value) { write_decimal(value); }
+#endif
+
+ template <typename T, typename Spec>
+ void write_int(T value, const Spec& spec) {
+ handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
+ }
+
+ template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+ void write(T value, format_specs specs = {}) {
+ 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.
+ fspecs.sign = sign::minus;
+ value = -value;
+ } else if (fspecs.sign == sign::minus) {
+ fspecs.sign = sign::none;
+ }
+
+ if (!std::isfinite(value)) {
+ auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
+ : (fspecs.upper ? "NAN" : "nan");
+ return write_padded(specs, nonfinite_writer<char_type>{fspecs.sign, str});
+ }
+
+ if (specs.align == align::none) {
+ specs.align = align::right;
+ } else if (specs.align == align::numeric) {
+ if (fspecs.sign) {
+ auto&& it = reserve(1);
+ *it++ = static_cast<char_type>(data::signs[fspecs.sign]);
+ fspecs.sign = sign::none;
+ if (specs.width != 0) --specs.width;
+ }
+ specs.align = align::right;
+ }
+
+ memory_buffer buffer;
+ 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);
+ write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
+ return;
+ }
+ int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
+ if (fspecs.format == float_format::exp) ++precision;
+ if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+ fspecs.use_grisu = use_grisu<T>();
+ if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
+ int exp = format_float(promote_float(value), precision, fspecs, buffer);
+ if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
+ buffer.push_back('%');
+ --exp; // Adjust decimal place position.
+ }
+ fspecs.precision = precision;
+ char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
+ : static_cast<char_type>('.');
+ write_padded(specs, float_writer<char_type>(buffer.data(),
+ static_cast<int>(buffer.size()),
+ exp, fspecs, point));
+ }
+
+ void write(char value) {
+ auto&& it = reserve(1);
+ *it++ = value;
+ }
+
+ template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
+ void write(Char value) {
+ auto&& it = reserve(1);
+ *it++ = value;
+ }
+
+ void write(string_view value) {
+ auto&& it = reserve(value.size());
+ it = copy_str<char_type>(value.begin(), value.end(), it);
+ }
+ void write(wstring_view value) {
+ static_assert(std::is_same<char_type, wchar_t>::value, "");
+ auto&& it = reserve(value.size());
+ it = std::copy(value.begin(), value.end(), it);
+ }
+
+ template <typename Char>
+ void write(const Char* s, std::size_t size, const format_specs& specs) {
+ write_padded(specs, str_writer<Char>{s, size});
+ }
+
+ template <typename Char>
+ void write(basic_string_view<Char> s, const format_specs& specs = {}) {
+ const Char* data = s.data();
+ std::size_t size = s.size();
+ if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+ size = code_point_index(s, to_unsigned(specs.precision));
+ write(data, size, specs);
+ }
+
+ template <typename UIntPtr>
+ void write_pointer(UIntPtr value, const format_specs* specs) {
+ int num_digits = count_digits<4>(value);
+ auto pw = pointer_writer<UIntPtr>{value, num_digits};
+ if (!specs) return pw(reserve(to_unsigned(num_digits) + 2));
+ format_specs specs_copy = *specs;
+ if (specs_copy.align == align::none) specs_copy.align = align::right;
+ write_padded(specs_copy, pw);
+ }
+};
+
+using writer = basic_writer<buffer_range<char>>;
+
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_t> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
public:
- typedef typename Range::value_type char_type;
- typedef decltype(std::declval<Range>().begin()) iterator;
- typedef basic_format_specs<char_type> format_specs;
+ using char_type = typename Range::value_type;
+ using iterator = typename Range::iterator;
+ using format_specs = basic_format_specs<char_type>;
private:
- typedef basic_writer<Range> writer_type;
+ using writer_type = basic_writer<Range>;
writer_type writer_;
format_specs* specs_;
@@ -1290,12 +1857,13 @@ class arg_formatter_base {
}
void write_pointer(const void* p) {
- writer_.write_pointer(internal::bit_cast<internal::uintptr_t>(p), specs_);
+ writer_.write_pointer(internal::to_uintptr(p), specs_);
}
protected:
writer_type& writer() { return writer_; }
- format_specs* spec() { return specs_; }
+ FMT_DEPRECATED format_specs* spec() { return specs_; }
+ format_specs* specs() { return specs_; }
iterator out() { return writer_.out(); }
void write(bool value) {
@@ -1304,10 +1872,13 @@ class arg_formatter_base {
}
void write(const char_type* value) {
- if (!value) FMT_THROW(format_error("string pointer is null"));
- auto length = std::char_traits<char_type>::length(value);
- basic_string_view<char_type> sv(value, length);
- specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+ if (!value) {
+ FMT_THROW(format_error("string pointer is null"));
+ } else {
+ auto length = std::char_traits<char_type>::length(value);
+ basic_string_view<char_type> sv(value, length);
+ specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+ }
}
public:
@@ -1319,7 +1890,7 @@ class arg_formatter_base {
return out();
}
- template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+ template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
iterator operator()(T value) {
if (specs_)
writer_.write_int(value, *specs_);
@@ -1342,7 +1913,7 @@ class arg_formatter_base {
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
- writer_.write_double(value, specs_ ? *specs_ : format_specs());
+ writer_.write(value, specs_ ? *specs_ : format_specs());
return out();
}
@@ -1405,17 +1976,16 @@ template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
// 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 <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR unsigned parse_nonnegative_int(const Char*& begin,
- const Char* end,
- ErrorHandler&& eh) {
- assert(begin != end && '0' <= *begin && *begin <= '9');
+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.
- unsigned max_int = (std::numeric_limits<int>::max)();
+ constexpr unsigned max_int = max_value<int>();
unsigned big = max_int / 10;
do {
// Check for overflow.
@@ -1427,18 +1997,18 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(const Char*& begin,
++begin;
} while (begin != end && '0' <= *begin && *begin <= '9');
if (value > max_int) eh.on_error("number is too big");
- return value;
+ return static_cast<int>(value);
}
template <typename Context> class custom_formatter {
private:
- typedef typename Context::char_type char_type;
+ using char_type = typename Context::char_type;
- basic_parse_context<char_type>& parse_ctx_;
+ basic_format_parse_context<char_type>& parse_ctx_;
Context& ctx_;
public:
- explicit custom_formatter(basic_parse_context<char_type>& parse_ctx,
+ explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
Context& ctx)
: parse_ctx_(parse_ctx), ctx_(ctx) {}
@@ -1450,12 +2020,11 @@ template <typename Context> class custom_formatter {
template <typename T> bool operator()(T) const { return false; }
};
-template <typename T> struct is_integer {
- enum {
- value = std::is_integral<T>::value && !std::is_same<T, bool>::value &&
- !std::is_same<T, char>::value && !std::is_same<T, wchar_t>::value
- };
-};
+template <typename T>
+using is_integer =
+ bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
+ !std::is_same<T, char>::value &&
+ !std::is_same<T, wchar_t>::value>;
template <typename ErrorHandler> class width_checker {
public:
@@ -1506,21 +2075,21 @@ template <typename Char> class specs_setter {
FMT_CONSTEXPR specs_setter(const specs_setter& other)
: specs_(other.specs_) {}
- FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; }
- FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; }
- FMT_CONSTEXPR void on_plus() { specs_.flags |= SIGN_FLAG | PLUS_FLAG; }
- FMT_CONSTEXPR void on_minus() { specs_.flags |= MINUS_FLAG; }
- FMT_CONSTEXPR void on_space() { specs_.flags |= SIGN_FLAG; }
- FMT_CONSTEXPR void on_hash() { specs_.flags |= HASH_FLAG; }
+ 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_plus() { specs_.sign = sign::plus; }
+ FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
+ FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
+ FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
FMT_CONSTEXPR void on_zero() {
- specs_.align_ = ALIGN_NUMERIC;
- specs_.fill_ = '0';
+ specs_.align = align::numeric;
+ specs_.fill[0] = Char('0');
}
- FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; }
- FMT_CONSTEXPR void on_precision(unsigned precision) {
- specs_.precision = static_cast<int>(precision);
+ FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
+ FMT_CONSTEXPR void on_precision(int precision) {
+ specs_.precision = precision;
}
FMT_CONSTEXPR void end_precision() {}
@@ -1538,20 +2107,20 @@ template <typename ErrorHandler> class numeric_specs_checker {
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void require_numeric_argument() {
- if (!is_arithmetic(arg_type_))
+ if (!is_arithmetic_type(arg_type_))
error_handler_.on_error("format specifier requires numeric argument");
}
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
- if (is_integral(arg_type_) && arg_type_ != int_type &&
+ if (is_integral_type(arg_type_) && arg_type_ != int_type &&
arg_type_ != long_long_type && arg_type_ != internal::char_type) {
error_handler_.on_error("format specifier requires signed argument");
}
}
FMT_CONSTEXPR void check_precision() {
- if (is_integral(arg_type_) || arg_type_ == internal::pointer_type)
+ if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type)
error_handler_.on_error("precision not allowed for this argument type");
}
@@ -1570,8 +2139,8 @@ template <typename Handler> class specs_checker : public Handler {
FMT_CONSTEXPR specs_checker(const specs_checker& other)
: Handler(other), checker_(*this, other.arg_type_) {}
- FMT_CONSTEXPR void on_align(alignment align) {
- if (align == ALIGN_NUMERIC) checker_.require_numeric_argument();
+ FMT_CONSTEXPR void on_align(align_t align) {
+ if (align == align::numeric) checker_.require_numeric_argument();
Handler::on_align(align);
}
@@ -1606,20 +2175,18 @@ template <typename Handler> class specs_checker : public Handler {
numeric_specs_checker<Handler> checker_;
};
-template <template <typename> class Handler, typename T, typename FormatArg,
+template <template <typename> class Handler, typename FormatArg,
typename ErrorHandler>
-FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) {
- unsigned long long big_value =
- visit_format_arg(Handler<ErrorHandler>(eh), arg);
- if (big_value > to_unsigned((std::numeric_limits<int>::max)()))
- eh.on_error("number is too big");
- value = static_cast<T>(big_value);
+FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
+ unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+ if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+ return static_cast<int>(value);
}
struct auto_id {};
template <typename Context>
-FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, unsigned id) {
+FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, int id) {
auto arg = ctx.arg(id);
if (!arg) ctx.on_error("argument index out of range");
return arg;
@@ -1629,7 +2196,7 @@ FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, unsigned id) {
template <typename ParseContext, typename Context>
class specs_handler : public specs_setter<typename Context::char_type> {
public:
- typedef typename Context::char_type char_type;
+ using char_type = typename Context::char_type;
FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
ParseContext& parse_ctx, Context& ctx)
@@ -1638,26 +2205,26 @@ class specs_handler : public specs_setter<typename Context::char_type> {
context_(ctx) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
- set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id),
- context_.error_handler());
+ this->specs_.width = get_dynamic_spec<width_checker>(
+ get_arg(arg_id), context_.error_handler());
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
- set_dynamic_spec<precision_checker>(this->specs_.precision, get_arg(arg_id),
- context_.error_handler());
+ this->specs_.precision = get_dynamic_spec<precision_checker>(
+ get_arg(arg_id), context_.error_handler());
}
void on_error(const char* message) { context_.on_error(message); }
private:
// This is only needed for compatibility with gcc 4.4.
- typedef typename Context::format_arg format_arg;
+ using format_arg = typename Context::format_arg;
FMT_CONSTEXPR format_arg get_arg(auto_id) {
return internal::get_arg(context_, parse_context_.next_arg_id());
}
- FMT_CONSTEXPR format_arg get_arg(unsigned arg_id) {
+ FMT_CONSTEXPR format_arg get_arg(int arg_id) {
parse_context_.check_arg_id(arg_id);
return internal::get_arg(context_, arg_id);
}
@@ -1671,47 +2238,29 @@ class specs_handler : public specs_setter<typename Context::char_type> {
Context& context_;
};
-struct string_view_metadata {
- FMT_CONSTEXPR string_view_metadata() : offset_(0u), size_(0u) {}
- template <typename Char>
- FMT_CONSTEXPR string_view_metadata(basic_string_view<Char> primary_string,
- basic_string_view<Char> view)
- : offset_(view.data() - primary_string.data()), size_(view.size()) {}
- FMT_CONSTEXPR string_view_metadata(std::size_t offset, std::size_t size)
- : offset_(offset), size_(size) {}
- template <typename Char>
- FMT_CONSTEXPR basic_string_view<Char> to_view(const Char* str) const {
- return {str + offset_, size_};
- }
-
- std::size_t offset_;
- std::size_t size_;
-};
+enum class arg_id_kind { none, index, name };
// An argument reference.
template <typename Char> struct arg_ref {
- enum Kind { NONE, INDEX, NAME };
- typedef Char char_type;
-
- FMT_CONSTEXPR arg_ref() : kind(NONE), val() {}
- FMT_CONSTEXPR explicit arg_ref(unsigned index) : kind(INDEX), val(index) {}
- FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
- : kind(NAME), val(name) {}
-
- FMT_CONSTEXPR arg_ref& operator=(unsigned idx) {
- kind = INDEX;
+ 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<Char> name)
+ : kind(arg_id_kind::name), val(name) {}
+
+ FMT_CONSTEXPR arg_ref& operator=(int idx) {
+ kind = arg_id_kind::index;
val.index = idx;
return *this;
}
- Kind kind;
+ arg_id_kind kind;
union value {
- FMT_CONSTEXPR value() : index(0u) {}
- FMT_CONSTEXPR value(unsigned id) : index(id) {}
- FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
+ FMT_CONSTEXPR value(int id = 0) : index{id} {}
+ FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
- unsigned index;
- string_view_metadata name;
+ int index;
+ basic_string_view<Char> name;
} val;
};
@@ -1730,7 +2279,7 @@ template <typename ParseContext>
class dynamic_specs_handler
: public specs_setter<typename ParseContext::char_type> {
public:
- typedef typename ParseContext::char_type char_type;
+ using char_type = typename ParseContext::char_type;
FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
ParseContext& ctx)
@@ -1754,9 +2303,9 @@ class dynamic_specs_handler
}
private:
- typedef arg_ref<char_type> arg_ref_type;
+ using arg_ref_type = arg_ref<char_type>;
- FMT_CONSTEXPR arg_ref_type make_arg_ref(unsigned arg_id) {
+ FMT_CONSTEXPR arg_ref_type make_arg_ref(int arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
@@ -1767,10 +2316,9 @@ class dynamic_specs_handler
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
context_.check_arg_id(arg_id);
- basic_string_view<char_type> format_str(context_.begin(),
- context_.end() - context_.begin());
- const auto id_metadata = string_view_metadata(format_str, arg_id);
- return arg_ref_type(id_metadata);
+ basic_string_view<char_type> format_str(
+ context_.begin(), to_unsigned(context_.end() - context_.begin()));
+ return arg_ref_type(arg_id);
}
dynamic_format_specs<char_type>& specs_;
@@ -1780,18 +2328,24 @@ class dynamic_specs_handler
template <typename Char, typename IDHandler>
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) {
- assert(begin != end);
+ FMT_ASSERT(begin != end, "");
Char c = *begin;
- if (c == '}' || c == ':') return handler(), begin;
+ if (c == '}' || c == ':') {
+ handler();
+ return begin;
+ }
if (c >= '0' && c <= '9') {
- unsigned index = parse_nonnegative_int(begin, end, handler);
+ int index = parse_nonnegative_int(begin, end, handler);
if (begin == end || (*begin != '}' && *begin != ':'))
- return handler.on_error("invalid format string"), begin;
- handler(index);
+ handler.on_error("invalid format string");
+ else
+ handler(index);
+ return begin;
+ }
+ if (!is_name_start(c)) {
+ handler.on_error("invalid format string");
return begin;
}
- if (!is_name_start(c))
- return handler.on_error("invalid format string"), begin;
auto it = begin;
do {
++it;
@@ -1805,7 +2359,7 @@ template <typename SpecHandler, typename Char> struct width_adapter {
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
- FMT_CONSTEXPR void operator()(unsigned id) { handler.on_dynamic_width(id); }
+ FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_width(id);
}
@@ -1822,9 +2376,7 @@ template <typename SpecHandler, typename Char> struct precision_adapter {
explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
- FMT_CONSTEXPR void operator()(unsigned id) {
- handler.on_dynamic_precision(id);
- }
+ FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_precision(id);
}
@@ -1841,25 +2393,27 @@ template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
Handler&& handler) {
FMT_ASSERT(begin != end, "");
- alignment align = ALIGN_DEFAULT;
+ auto align = align::none;
int i = 0;
if (begin + 1 != end) ++i;
do {
switch (static_cast<char>(begin[i])) {
case '<':
- align = ALIGN_LEFT;
+ align = align::left;
break;
case '>':
- align = ALIGN_RIGHT;
+ align = align::right;
break;
+#if FMT_NUMERIC_ALIGN
case '=':
- align = ALIGN_NUMERIC;
+ align = align::numeric;
break;
+#endif
case '^':
- align = ALIGN_CENTER;
+ align = align::center;
break;
}
- if (align != ALIGN_DEFAULT) {
+ if (align != align::none) {
if (i > 0) {
auto c = *begin;
if (c == '{')
@@ -1984,7 +2538,7 @@ inline bool find<false, char>(const char* first, const char* last, char value,
template <typename Handler, typename Char> struct id_adapter {
FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
- FMT_CONSTEXPR void operator()(unsigned id) { handler.on_arg_id(id); }
+ FMT_CONSTEXPR void operator()(int id) { handler.on_arg_id(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_arg_id(id);
}
@@ -1997,7 +2551,7 @@ template <typename Handler, typename Char> struct id_adapter {
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) {
- struct writer {
+ struct pfs_writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
@@ -2055,10 +2609,9 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
conditional_t<internal::mapped_type_constant<T, context>::value !=
internal::custom_type,
decltype(arg_mapper<context>().map(std::declval<T>())), T>;
- conditional_t<has_formatter<mapped_type, context>::value,
- formatter<mapped_type, char_type>,
- internal::fallback_formatter<T, char_type>>
- f;
+ auto f = conditional_t<has_formatter<mapped_type, context>::value,
+ formatter<mapped_type, char_type>,
+ internal::fallback_formatter<T, char_type>>();
return f.parse(ctx);
}
@@ -2067,7 +2620,7 @@ class format_string_checker {
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
- : arg_id_((std::numeric_limits<unsigned>::max)()),
+ : arg_id_(max_value<unsigned>()),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
@@ -2077,7 +2630,7 @@ class format_string_checker {
arg_id_ = context_.next_arg_id();
check_arg_id();
}
- FMT_CONSTEXPR void on_arg_id(unsigned id) {
+ FMT_CONSTEXPR void on_arg_id(int id) {
arg_id_ = id;
context_.check_arg_id(id);
check_arg_id();
@@ -2090,7 +2643,7 @@ class format_string_checker {
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
advance_to(context_, begin);
- return arg_id_ < NUM_ARGS ? parse_funcs_[arg_id_](context_) : begin;
+ return arg_id_ < num_args ? parse_funcs_[arg_id_](context_) : begin;
}
FMT_CONSTEXPR void on_error(const char* message) {
@@ -2098,19 +2651,19 @@ class format_string_checker {
}
private:
- typedef basic_parse_context<Char, ErrorHandler> parse_context_type;
- enum { NUM_ARGS = sizeof...(Args) };
+ using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
+ enum { num_args = sizeof...(Args) };
FMT_CONSTEXPR void check_arg_id() {
- if (arg_id_ >= NUM_ARGS) context_.on_error("argument index out of range");
+ if (arg_id_ >= num_args) context_.on_error("argument index out of range");
}
// Format specifier parsing function.
- typedef const Char* (*parse_func)(parse_context_type&);
+ using parse_func = const Char* (*)(parse_context_type&);
unsigned arg_id_;
parse_context_type context_;
- parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1];
+ parse_func parse_funcs_[num_args > 0 ? num_args : 1];
};
template <typename Char, typename ErrorHandler, typename... Args>
@@ -2124,73 +2677,74 @@ FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
void check_format_string(S format_str) {
- typedef typename S::char_type char_t;
FMT_CONSTEXPR_DECL bool invalid_format =
- internal::do_check_format_string<char_t, internal::error_handler,
- Args...>(to_string_view(format_str));
+ internal::do_check_format_string<typename S::char_type,
+ internal::error_handler, Args...>(
+ to_string_view(format_str));
(void)invalid_format;
}
-template <template <typename> class Handler, typename Spec, typename Context>
-void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
- Context& ctx,
- const typename Context::char_type* format_str) {
- typedef typename Context::char_type char_type;
+template <template <typename> class Handler, typename Context>
+void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
+ Context& ctx) {
switch (ref.kind) {
- case arg_ref<char_type>::NONE:
+ case arg_id_kind::none:
break;
- case arg_ref<char_type>::INDEX:
- internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
- ctx.error_handler());
+ case arg_id_kind::index:
+ value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+ ctx.error_handler());
+ break;
+ case arg_id_kind::name:
+ value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+ ctx.error_handler());
break;
- case arg_ref<char_type>::NAME: {
- const auto arg_id = ref.val.name.to_view(format_str);
- internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id),
- ctx.error_handler());
- } break;
}
}
} // namespace internal
+template <typename Range>
+using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
+using writer FMT_DEPRECATED_ALIAS = internal::writer;
+using wwriter FMT_DEPRECATED_ALIAS =
+ internal::basic_writer<buffer_range<wchar_t>>;
+
/** The default argument formatter. */
template <typename Range>
class arg_formatter : public internal::arg_formatter_base<Range> {
private:
- typedef typename Range::value_type char_type;
- typedef internal::arg_formatter_base<Range> base;
- typedef basic_format_context<typename base::iterator, char_type> context_type;
+ using char_type = typename Range::value_type;
+ using base = internal::arg_formatter_base<Range>;
+ using context_type = basic_format_context<typename base::iterator, char_type>;
context_type& ctx_;
- basic_parse_context<char_type>* parse_ctx_;
+ basic_format_parse_context<char_type>* parse_ctx_;
public:
- typedef Range range;
- typedef typename base::iterator iterator;
- typedef typename base::format_specs format_specs;
+ using range = Range;
+ using iterator = typename base::iterator;
+ using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
- *spec* contains format specifier information for standard argument types.
+ *specs* contains format specifier information for standard argument types.
\endrst
*/
- explicit arg_formatter(context_type& ctx,
- basic_parse_context<char_type>* parse_ctx = nullptr,
- format_specs* spec = nullptr)
- : base(Range(ctx.out()), spec, ctx.locale()),
+ explicit arg_formatter(
+ context_type& ctx,
+ basic_format_parse_context<char_type>* parse_ctx = nullptr,
+ format_specs* specs = nullptr)
+ : base(Range(ctx.out()), specs, ctx.locale()),
ctx_(ctx),
parse_ctx_(parse_ctx) {}
- FMT_DEPRECATED arg_formatter(context_type& ctx, format_specs& spec)
- : base(Range(ctx.out()), &spec), ctx_(ctx) {}
-
using base::operator();
/** Formats an argument of a user-defined type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(*parse_ctx_, ctx_);
- return this->out();
+ return ctx_.out();
}
};
@@ -2200,12 +2754,12 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
*/
class FMT_API system_error : public std::runtime_error {
private:
- FMT_API void init(int err_code, string_view format_str, format_args args);
+ void init(int err_code, string_view format_str, format_args args);
protected:
int error_code_;
- system_error() : std::runtime_error("") {}
+ system_error() : std::runtime_error(""), error_code_(0) {}
public:
/**
@@ -2231,7 +2785,11 @@ class FMT_API system_error : public std::runtime_error {
: std::runtime_error("") {
init(error_code, message, make_format_args(args...));
}
- ~system_error() FMT_NOEXCEPT;
+ system_error(const system_error&) = default;
+ system_error& operator=(const system_error&) = default;
+ system_error(system_error&&) = default;
+ system_error& operator=(system_error&&) = default;
+ ~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
int error_code() const { return error_code_; }
};
@@ -2253,532 +2811,7 @@ class FMT_API system_error : public std::runtime_error {
\endrst
*/
FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
- fmt::string_view message) FMT_NOEXCEPT;
-
-/**
- This template provides operations for formatting and writing data into a
- character range.
- */
-template <typename Range> class basic_writer {
- public:
- typedef typename Range::value_type char_type;
- typedef decltype(std::declval<Range>().begin()) iterator;
- typedef basic_format_specs<char_type> format_specs;
-
- private:
- iterator out_; // Output iterator.
- internal::locale_ref locale_;
-
- // Attempts to reserve space for n extra characters in the output range.
- // Returns a pointer to the reserved range or a reference to out_.
- auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
- return internal::reserve(out_, n);
- }
-
- // Writes a value in the format
- // <left-padding><value><right-padding>
- // where <value> is written by f(it).
- template <typename F> void write_padded(const align_spec& spec, F&& f) {
- unsigned width = spec.width(); // User-perceived width (in code points).
- 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 = static_cast<char_type>(spec.fill());
- std::size_t padding = width - num_code_points;
- if (spec.align() == ALIGN_RIGHT) {
- it = std::fill_n(it, padding, fill);
- f(it);
- } else if (spec.align() == ALIGN_CENTER) {
- std::size_t left_padding = padding / 2;
- it = std::fill_n(it, left_padding, fill);
- f(it);
- it = std::fill_n(it, padding - left_padding, fill);
- } else {
- f(it);
- it = std::fill_n(it, padding, fill);
- }
- }
-
- template <typename F> struct padded_int_writer {
- size_t size_;
- string_view prefix;
- char_type fill;
- std::size_t padding;
- F f;
-
- size_t size() const { return size_; }
- size_t width() const { return size_; }
-
- template <typename It> void operator()(It&& it) const {
- if (prefix.size() != 0)
- it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
- it = std::fill_n(it, padding, fill);
- f(it);
- }
- };
-
- // Writes an integer in the format
- // <left-padding><prefix><numeric-padding><digits><right-padding>
- // where <digits> are written by f(it).
- template <typename Spec, typename F>
- void write_int(int num_digits, string_view prefix, const Spec& spec, F f) {
- std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
- char_type fill = static_cast<char_type>(spec.fill());
- std::size_t padding = 0;
- if (spec.align() == ALIGN_NUMERIC) {
- if (spec.width() > size) {
- padding = spec.width() - size;
- size = spec.width();
- }
- } else if (spec.precision > num_digits) {
- size = prefix.size() + internal::to_unsigned(spec.precision);
- padding = internal::to_unsigned(spec.precision - num_digits);
- fill = static_cast<char_type>('0');
- }
- align_spec as = spec;
- if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
- write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
- }
-
- // Writes a decimal integer.
- template <typename Int> void write_decimal(Int value) {
- typedef typename internal::int_traits<Int>::main_type main_type;
- main_type abs_value = static_cast<main_type>(value);
- bool is_negative = internal::is_negative(value);
- if (is_negative) abs_value = 0 - abs_value;
- int num_digits = internal::count_digits(abs_value);
- auto&& it =
- reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
- if (is_negative) *it++ = static_cast<char_type>('-');
- it = internal::format_decimal<char_type>(it, abs_value, num_digits);
- }
-
- // The handle_int_type_spec handler that writes an integer.
- template <typename Int, typename Spec> struct int_writer {
- typedef typename internal::int_traits<Int>::main_type unsigned_type;
-
- basic_writer<Range>& writer;
- const Spec& spec;
- unsigned_type abs_value;
- char prefix[4];
- unsigned prefix_size;
-
- string_view get_prefix() const { return string_view(prefix, prefix_size); }
-
- int_writer(basic_writer<Range>& w, Int value, const Spec& s)
- : writer(w),
- spec(s),
- abs_value(static_cast<unsigned_type>(value)),
- prefix_size(0) {
- if (internal::is_negative(value)) {
- prefix[0] = '-';
- ++prefix_size;
- abs_value = 0 - abs_value;
- } else if (spec.has(SIGN_FLAG)) {
- prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' ';
- ++prefix_size;
- }
- }
-
- struct dec_writer {
- unsigned_type abs_value;
- int num_digits;
-
- template <typename It> void operator()(It&& it) const {
- it = internal::format_decimal<char_type>(it, abs_value, num_digits);
- }
- };
-
- void on_dec() {
- int num_digits = internal::count_digits(abs_value);
- writer.write_int(num_digits, get_prefix(), spec,
- dec_writer{abs_value, num_digits});
- }
-
- struct hex_writer {
- int_writer& self;
- int num_digits;
-
- template <typename It> void operator()(It&& it) const {
- it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits,
- self.spec.type != 'x');
- }
- };
-
- void on_hex() {
- if (spec.has(HASH_FLAG)) {
- prefix[prefix_size++] = '0';
- prefix[prefix_size++] = static_cast<char>(spec.type);
- }
- int num_digits = internal::count_digits<4>(abs_value);
- writer.write_int(num_digits, get_prefix(), spec,
- hex_writer{*this, num_digits});
- }
-
- template <int BITS> struct bin_writer {
- unsigned_type abs_value;
- int num_digits;
-
- template <typename It> void operator()(It&& it) const {
- it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
- }
- };
-
- void on_bin() {
- if (spec.has(HASH_FLAG)) {
- prefix[prefix_size++] = '0';
- prefix[prefix_size++] = static_cast<char>(spec.type);
- }
- int num_digits = internal::count_digits<1>(abs_value);
- writer.write_int(num_digits, get_prefix(), spec,
- bin_writer<1>{abs_value, num_digits});
- }
-
- void on_oct() {
- int num_digits = internal::count_digits<3>(abs_value);
- if (spec.has(HASH_FLAG) && spec.precision <= num_digits) {
- // Octal prefix '0' is counted as a digit, so only add it if precision
- // is not greater than the number of digits.
- prefix[prefix_size++] = '0';
- }
- writer.write_int(num_digits, get_prefix(), spec,
- bin_writer<3>{abs_value, num_digits});
- }
-
- enum { SEP_SIZE = 1 };
-
- struct num_writer {
- unsigned_type abs_value;
- int size;
- char_type sep;
-
- template <typename It> void operator()(It&& it) const {
- basic_string_view<char_type> s(&sep, SEP_SIZE);
- it = internal::format_decimal<char_type>(
- it, abs_value, size, internal::add_thousands_sep<char_type>(s));
- }
- };
-
- void on_num() {
- int num_digits = internal::count_digits(abs_value);
- char_type sep = internal::thousands_sep<char_type>(writer.locale_);
- int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
- writer.write_int(size, get_prefix(), spec,
- num_writer{abs_value, size, sep});
- }
-
- FMT_NORETURN void on_error() {
- FMT_THROW(format_error("invalid type specifier"));
- }
- };
-
- // Writes a formatted integer.
- template <typename T, typename Spec>
- void write_int(T value, const Spec& spec) {
- internal::handle_int_type_spec(spec.type,
- int_writer<T, Spec>(*this, value, spec));
- }
-
- enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC.
-
- struct inf_or_nan_writer {
- char sign;
- bool as_percentage;
- const char* str;
-
- size_t size() const {
- return static_cast<std::size_t>(INF_SIZE + (sign ? 1 : 0) +
- (as_percentage ? 1 : 0));
- }
- size_t width() const { return size(); }
-
- template <typename It> void operator()(It&& it) const {
- if (sign) *it++ = static_cast<char_type>(sign);
- it = internal::copy_str<char_type>(
- str, str + static_cast<std::size_t>(INF_SIZE), it);
- if (as_percentage) *it++ = static_cast<char_type>('%');
- }
- };
-
- struct double_writer {
- char sign;
- internal::buffer<char>& buffer;
-
- size_t size() const { return buffer.size() + (sign ? 1 : 0); }
- size_t width() const { return size(); }
-
- template <typename It> void operator()(It&& it) {
- if (sign) *it++ = static_cast<char_type>(sign);
- it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
- }
- };
-
- class grisu_writer {
- private:
- internal::buffer<char>& digits_;
- size_t size_;
- char sign_;
- int exp_;
- internal::gen_digits_params params_;
-
- public:
- grisu_writer(char sign, internal::buffer<char>& digits, int exp,
- const internal::gen_digits_params& params)
- : digits_(digits), sign_(sign), exp_(exp), params_(params) {
- int num_digits = static_cast<int>(digits.size());
- int full_exp = num_digits + exp - 1;
- int precision = params.num_digits > 0 ? params.num_digits : 11;
- params_.fixed |= full_exp >= -4 && full_exp < precision;
- auto it = internal::grisu_prettify<char>(
- digits.data(), num_digits, exp, internal::counting_iterator<char>(),
- params_);
- size_ = it.count();
- }
-
- size_t size() const { return size_ + (sign_ ? 1 : 0); }
- size_t width() const { return size(); }
-
- template <typename It> void operator()(It&& it) {
- if (sign_) *it++ = static_cast<char_type>(sign_);
- int num_digits = static_cast<int>(digits_.size());
- it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
- it, params_);
- }
- };
-
- // Formats a floating-point number (double or long double).
- template <typename T> void write_double(T value, const format_specs& spec);
-
- template <typename Char> struct str_writer {
- const Char* s;
- size_t size_;
-
- size_t size() const { return size_; }
- size_t width() const {
- return internal::count_code_points(basic_string_view<Char>(s, size_));
- }
-
- template <typename It> void operator()(It&& it) const {
- it = internal::copy_str<char_type>(s, s + size_, it);
- }
- };
-
- template <typename UIntPtr> struct pointer_writer {
- UIntPtr value;
- int num_digits;
-
- size_t size() const { return num_digits + 2; }
- size_t width() const { return size(); }
-
- template <typename It> void operator()(It&& it) const {
- *it++ = static_cast<char_type>('0');
- *it++ = static_cast<char_type>('x');
- it = internal::format_uint<4, char_type>(it, value, num_digits);
- }
- };
-
- template <typename Char, typename ErrorHandler>
- friend class internal::arg_formatter_base;
-
- public:
- /** Constructs a ``basic_writer`` object. */
- explicit basic_writer(Range out,
- internal::locale_ref loc = internal::locale_ref())
- : out_(out.begin()), locale_(loc) {}
-
- iterator out() const { return out_; }
-
- void write(int value) { write_decimal(value); }
- void write(long value) { write_decimal(value); }
- void write(long long value) { write_decimal(value); }
-
- void write(unsigned value) { write_decimal(value); }
- void write(unsigned long value) { write_decimal(value); }
- void write(unsigned long long value) { write_decimal(value); }
-
- /**
- \rst
- Formats *value* and writes it to the buffer.
- \endrst
- */
- template <typename T, typename FormatSpec, typename... FormatSpecs,
- FMT_ENABLE_IF(std::is_integral<T>::value)>
- void write(T value, FormatSpec spec, FormatSpecs... specs) {
- format_specs s(spec, specs...);
- s.align_ = ALIGN_RIGHT;
- write_int(value, s);
- }
-
- void write(double value, const format_specs& spec = format_specs()) {
- write_double(value, spec);
- }
-
- /**
- \rst
- Formats *value* using the general format for floating-point numbers
- (``'g'``) and writes it to the buffer.
- \endrst
- */
- void write(long double value, const format_specs& spec = format_specs()) {
- write_double(value, spec);
- }
-
- /** Writes a character to the buffer. */
- void write(char value) {
- auto&& it = reserve(1);
- *it++ = value;
- }
-
- template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
- void write(Char value) {
- auto&& it = reserve(1);
- *it++ = value;
- }
-
- /**
- \rst
- Writes *value* to the buffer.
- \endrst
- */
- void write(string_view value) {
- auto&& it = reserve(value.size());
- it = internal::copy_str<char_type>(value.begin(), value.end(), it);
- }
- void write(wstring_view value) {
- static_assert(std::is_same<char_type, wchar_t>::value, "");
- auto&& it = reserve(value.size());
- it = std::copy(value.begin(), value.end(), it);
- }
-
- // Writes a formatted string.
- template <typename Char>
- void write(const Char* s, std::size_t size, const align_spec& spec) {
- write_padded(spec, str_writer<Char>{s, size});
- }
-
- template <typename Char>
- void write(basic_string_view<Char> s,
- const format_specs& spec = format_specs()) {
- const Char* data = s.data();
- std::size_t size = s.size();
- if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size)
- size = internal::to_unsigned(spec.precision);
- write(data, size, spec);
- }
-
- template <typename UIntPtr>
- void write_pointer(UIntPtr value, const align_spec* spec) {
- int num_digits = internal::count_digits<4>(value);
- auto pw = pointer_writer<UIntPtr>{value, num_digits};
- if (!spec) return pw(reserve(num_digits + 2));
- align_spec as = *spec;
- if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
- write_padded(as, pw);
- }
-};
-
-struct float_spec_handler {
- char type;
- bool upper;
- bool fixed;
- bool as_percentage;
-
- explicit float_spec_handler(char t)
- : type(t), upper(false), fixed(false), as_percentage(false) {}
-
- void on_general() {
- if (type == 'G') upper = true;
- }
-
- void on_exp() {
- if (type == 'E') upper = true;
- }
-
- void on_fixed() {
- fixed = true;
- if (type == 'F') upper = true;
- }
-
- void on_percent() {
- fixed = true;
- as_percentage = true;
- }
-
- void on_hex() {
- if (type == 'A') upper = true;
- }
-
- FMT_NORETURN void on_error() {
- FMT_THROW(format_error("invalid type specifier"));
- }
-};
-
-template <typename Range>
-template <typename T>
-void basic_writer<Range>::write_double(T value, const format_specs& spec) {
- // Check type.
- float_spec_handler handler(static_cast<char>(spec.type));
- internal::handle_float_type_spec(handler.type, handler);
-
- char sign = 0;
- // Use signbit instead of value < 0 since the latter is always false for NaN.
- if (std::signbit(value)) {
- sign = '-';
- value = -value;
- } else if (spec.has(SIGN_FLAG)) {
- sign = spec.has(PLUS_FLAG) ? '+' : ' ';
- }
-
- if (!std::isfinite(value)) {
- // Format infinity and NaN ourselves because sprintf's output is not
- // consistent across platforms.
- const char* str = std::isinf(value) ? (handler.upper ? "INF" : "inf")
- : (handler.upper ? "NAN" : "nan");
- return write_padded(spec,
- inf_or_nan_writer{sign, handler.as_percentage, str});
- }
-
- if (handler.as_percentage) value *= 100;
-
- memory_buffer buffer;
- int exp = 0;
- int precision = spec.has_precision() || !spec.type ? spec.precision : 6;
- unsigned options = handler.fixed ? internal::grisu_options::fixed : 0;
- bool use_grisu = fmt::internal::use_grisu<T>() &&
- (spec.type != 'a' && spec.type != 'A' && spec.type != 'e' &&
- spec.type != 'E') &&
- internal::grisu_format(static_cast<double>(value), buffer,
- precision, options, exp);
- if (!use_grisu) internal::sprintf_format(value, buffer, spec);
-
- if (handler.as_percentage) {
- buffer.push_back('%');
- --exp; // Adjust decimal place position.
- }
- align_spec as = spec;
- if (spec.align() == ALIGN_NUMERIC) {
- if (sign) {
- auto&& it = reserve(1);
- *it++ = static_cast<char_type>(sign);
- sign = 0;
- if (as.width_) --as.width_;
- }
- as.align_ = ALIGN_RIGHT;
- } else if (spec.align() == ALIGN_DEFAULT) {
- as.align_ = ALIGN_RIGHT;
- }
- if (use_grisu) {
- auto params = internal::gen_digits_params();
- params.fixed = handler.fixed;
- params.num_digits = precision;
- params.trailing_zeros = (precision != 0 && (handler.fixed || !spec.type)) ||
- spec.has(HASH_FLAG);
- write_padded(as, grisu_writer{sign, buffer, exp, params});
- } else {
- write_padded(as, double_writer{sign, buffer});
- }
-}
+ string_view message) FMT_NOEXCEPT;
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
@@ -2839,34 +2872,34 @@ class format_int {
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
- enum { BUFFER_SIZE = std::numeric_limits<unsigned long long>::digits10 + 3 };
- mutable char buffer_[BUFFER_SIZE];
+ enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+ mutable char buffer_[buffer_size];
char* str_;
// Formats value in reverse and returns a pointer to the beginning.
char* format_decimal(unsigned long long value) {
- char* ptr = buffer_ + (BUFFER_SIZE - 1); // Parens to workaround MSVC bug.
+ char* ptr = buffer_ + (buffer_size - 1); // Parens to workaround MSVC bug.
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
- unsigned index = static_cast<unsigned>((value % 100) * 2);
+ auto index = static_cast<unsigned>((value % 100) * 2);
value /= 100;
- *--ptr = internal::data::DIGITS[index + 1];
- *--ptr = internal::data::DIGITS[index];
+ *--ptr = internal::data::digits[index + 1];
+ *--ptr = internal::data::digits[index];
}
if (value < 10) {
*--ptr = static_cast<char>('0' + value);
return ptr;
}
- unsigned index = static_cast<unsigned>(value * 2);
- *--ptr = internal::data::DIGITS[index + 1];
- *--ptr = internal::data::DIGITS[index];
+ auto index = static_cast<unsigned>(value * 2);
+ *--ptr = internal::data::digits[index + 1];
+ *--ptr = internal::data::digits[index];
return ptr;
}
void format_signed(long long value) {
- unsigned long long abs_value = static_cast<unsigned long long>(value);
+ auto abs_value = static_cast<unsigned long long>(value);
bool negative = value < 0;
if (negative) abs_value = 0 - abs_value;
str_ = format_decimal(abs_value);
@@ -2883,7 +2916,7 @@ class format_int {
/** Returns the number of characters written to the output buffer. */
std::size_t size() const {
- return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1);
+ return internal::to_unsigned(buffer_ - str_ + buffer_size - 1);
}
/**
@@ -2897,7 +2930,7 @@ class format_int {
character appended.
*/
const char* c_str() const {
- buffer_[BUFFER_SIZE - 1] = '\0';
+ buffer_[buffer_size - 1] = '\0';
return str_;
}
@@ -2915,13 +2948,12 @@ template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<internal::type_constant<T, Char>::value !=
internal::custom_type>> {
- FMT_CONSTEXPR formatter() : format_str_(nullptr) {}
+ FMT_CONSTEXPR formatter() = default;
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
- format_str_ = ctx.begin();
using handler_type = internal::dynamic_specs_handler<ParseContext>;
auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
@@ -2937,6 +2969,8 @@ struct formatter<T, Char,
case internal::uint_type:
case internal::long_long_type:
case internal::ulong_long_type:
+ case internal::int128_type:
+ case internal::uint128_type:
case internal::bool_type:
handle_int_type_spec(specs_.type,
internal::int_type_checker<decltype(eh)>(eh));
@@ -2945,10 +2979,10 @@ struct formatter<T, Char,
handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
break;
+ case internal::float_type:
case internal::double_type:
case internal::long_double_type:
- handle_float_type_spec(specs_.type,
- internal::float_type_checker<decltype(eh)>(eh));
+ internal::parse_float_type_spec(specs_, eh);
break;
case internal::cstring_type:
internal::handle_cstring_type_spec(
@@ -2971,9 +3005,9 @@ struct formatter<T, Char,
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
internal::handle_dynamic_spec<internal::width_checker>(
- specs_.width_, specs_.width_ref, ctx, format_str_);
+ specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
- specs_.precision, specs_.precision_ref, ctx, format_str_);
+ specs_.precision, specs_.precision_ref, ctx);
using range_type =
internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type>;
@@ -2983,7 +3017,6 @@ struct formatter<T, Char,
private:
internal::dynamic_format_specs<Char> specs_;
- const Char* format_str_;
};
#define FMT_FORMAT_AS(Type, Base) \
@@ -3001,7 +3034,6 @@ FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS(unsigned short, unsigned);
FMT_FORMAT_AS(long, long long);
FMT_FORMAT_AS(unsigned long, unsigned long long);
-FMT_FORMAT_AS(float, double);
FMT_FORMAT_AS(Char*, const Char*);
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(std::nullptr_t, const void*);
@@ -3026,7 +3058,7 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
// A formatter for types known only at run time such as variant alternatives.
//
// Usage:
-// typedef std::variant<int, std::string> variant;
+// using variant = std::variant<int, std::string>;
// template <>
// struct formatter<variant>: dynamic_formatter<> {
// void format(buffer &buf, const variant &v, context &ctx) {
@@ -3036,7 +3068,7 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
template <typename Char = char> class dynamic_formatter {
private:
struct null_handler : internal::error_handler {
- void on_align(alignment) {}
+ void on_align(align_t) {}
void on_plus() {}
void on_minus() {}
void on_space() {}
@@ -3058,19 +3090,24 @@ template <typename Char = char> class dynamic_formatter {
internal::specs_checker<null_handler> checker(
null_handler(),
internal::mapped_type_constant<T, FormatContext>::value);
- checker.on_align(specs_.align());
- if (specs_.flags == 0)
- ; // Do nothing.
- else if (specs_.has(SIGN_FLAG))
- specs_.has(PLUS_FLAG) ? checker.on_plus() : checker.on_space();
- else if (specs_.has(MINUS_FLAG))
+ checker.on_align(specs_.align);
+ switch (specs_.sign) {
+ case sign::none:
+ break;
+ case sign::plus:
+ checker.on_plus();
+ break;
+ case sign::minus:
checker.on_minus();
- else if (specs_.has(HASH_FLAG))
- checker.on_hash();
- if (specs_.precision != -1) checker.end_precision();
- typedef internal::output_range<typename FormatContext::iterator,
- typename FormatContext::char_type>
- range;
+ break;
+ case sign::space:
+ checker.on_space();
+ break;
+ }
+ if (specs_.alt) checker.on_hash();
+ if (specs_.precision >= 0) checker.end_precision();
+ using range = internal::output_range<typename FormatContext::iterator,
+ typename FormatContext::char_type>;
visit_format_arg(arg_formatter<range>(ctx, nullptr, &specs_),
internal::make_arg<FormatContext>(val));
return ctx.out();
@@ -3079,9 +3116,9 @@ template <typename Char = char> class dynamic_formatter {
private:
template <typename Context> void handle_specs(Context& ctx) {
internal::handle_dynamic_spec<internal::width_checker>(
- specs_.width_, specs_.width_ref, ctx, format_str_);
+ specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
- specs_.precision, specs_.precision_ref, ctx, format_str_);
+ specs_.precision, specs_.precision_ref, ctx);
}
internal::dynamic_format_specs<Char> specs_;
@@ -3098,14 +3135,14 @@ basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
}
template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void advance_to(basic_parse_context<Char, ErrorHandler>& ctx,
- const Char* p) {
+FMT_CONSTEXPR void advance_to(
+ basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
}
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : internal::error_handler {
- typedef typename ArgFormatter::range range;
+ using range = typename ArgFormatter::range;
format_handler(range r, basic_string_view<Char> str,
basic_format_args<Context> format_args,
@@ -3120,10 +3157,10 @@ struct format_handler : internal::error_handler {
context.advance_to(out);
}
- void get_arg(unsigned id) { arg = internal::get_arg(context, id); }
+ void get_arg(int id) { arg = internal::get_arg(context, id); }
void on_arg_id() { get_arg(parse_context.next_arg_id()); }
- void on_arg_id(unsigned id) {
+ void on_arg_id(int id) {
parse_context.check_arg_id(id);
get_arg(id);
}
@@ -3131,10 +3168,8 @@ struct format_handler : internal::error_handler {
void on_replacement_field(const Char* p) {
advance_to(parse_context, p);
- internal::custom_formatter<Context> f(parse_context, context);
- if (!visit_format_arg(f, arg))
- context.advance_to(
- visit_format_arg(ArgFormatter(context, &parse_context), arg));
+ context.advance_to(
+ visit_format_arg(ArgFormatter(context, &parse_context), arg));
}
const Char* on_format_specs(const Char* begin, const Char* end) {
@@ -3143,7 +3178,7 @@ struct format_handler : internal::error_handler {
if (visit_format_arg(f, arg)) return parse_context.begin();
basic_format_specs<Char> specs;
using internal::specs_handler;
- typedef basic_parse_context<Char> parse_context_t;
+ using parse_context_t = basic_format_parse_context<Char>;
internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
specs_handler<parse_context_t, Context>(specs, parse_context, context),
arg.type());
@@ -3155,7 +3190,7 @@ struct format_handler : internal::error_handler {
return begin;
}
- basic_parse_context<Char> parse_context;
+ basic_format_parse_context<Char> parse_context;
Context context;
basic_format_arg<Context> arg;
};
@@ -3182,10 +3217,12 @@ template <typename T> inline const void* ptr(const std::shared_ptr<T>& p) {
return p.get();
}
-template <typename It, typename Char> struct arg_join {
+template <typename It, typename Char> struct arg_join : internal::view {
It begin;
It end;
basic_string_view<Char> sep;
+
+ arg_join(It b, It e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
};
template <typename It, typename Char>
@@ -3194,7 +3231,7 @@ struct formatter<arg_join<It, Char>, Char>
template <typename FormatContext>
auto format(const arg_join<It, Char>& value, FormatContext& ctx)
-> decltype(ctx.out()) {
- typedef formatter<typename std::iterator_traits<It>::value_type, Char> base;
+ using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
auto it = value.begin;
auto out = ctx.out();
if (it != value.end) {
@@ -3223,8 +3260,6 @@ arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
return {begin, end, sep};
}
-// gcc 4.4 on join: internal compiler error: in tsubst_copy, at cp/pt.c:10122
-#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 405
/**
\rst
Returns an object that formats `range` with elements separated by `sep`.
@@ -3247,7 +3282,6 @@ arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
wstring_view sep) {
return join(std::begin(range), std::end(range), sep);
}
-#endif
/**
\rst
@@ -3297,78 +3331,26 @@ inline typename buffer_context<Char>::iterator vformat_to(
template <typename S, typename... Args, std::size_t SIZE = inline_buffer_size,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline typename buffer_context<Char>::iterator format_to(
- basic_memory_buffer<Char, SIZE>& buf, const S& format_str,
- const Args&... args) {
+ basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<Char>;
- format_arg_store<context, Args...> as{args...};
return internal::vformat_to(buf, to_string_view(format_str),
- basic_format_args<context>(as));
+ {make_format_args<context>(args...)});
}
-namespace internal {
-
-// Detect the iterator category of *any* given type in a SFINAE-friendly way.
-// Unfortunately, older implementations of std::iterator_traits are not safe
-// for use in a SFINAE-context.
-
-// the gist of C++17's void_t magic
-template <typename... Ts> struct void_ { typedef void type; };
-
-template <typename T, typename Enable = void>
-struct it_category : std::false_type {};
-
-template <typename T> struct it_category<T*> {
- typedef std::random_access_iterator_tag type;
-};
-
-template <typename T>
-struct it_category<T, typename void_<typename T::iterator_category>::type> {
- typedef typename T::iterator_category type;
-};
-
-// Detect if *any* given type models the OutputIterator concept.
-template <typename It> class is_output_iterator {
- // Check for mutability because all iterator categories derived from
- // std::input_iterator_tag *may* also meet the requirements of an
- // OutputIterator, thereby falling into the category of 'mutable iterators'
- // [iterator.requirements.general] clause 4.
- // The compiler reveals this property only at the point of *actually
- // dereferencing* the iterator!
- template <typename U>
- static decltype(*(std::declval<U>())) test(std::input_iterator_tag);
- template <typename U> static char& test(std::output_iterator_tag);
- template <typename U> static const char& test(...);
-
- typedef decltype(test<It>(typename it_category<It>::type{})) type;
- typedef remove_reference_t<type> result;
-
- public:
- static const bool value = !std::is_const<result>::value;
-};
-} // namespace internal
-
template <typename OutputIt, typename Char = char>
-// using format_context_t = basic_format_context<OutputIt, Char>;
-struct format_context_t {
- typedef basic_format_context<OutputIt, Char> type;
-};
+using format_context_t = basic_format_context<OutputIt, Char>;
template <typename OutputIt, typename Char = char>
-// using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
-struct format_args_t {
- typedef basic_format_args<typename format_context_t<OutputIt, Char>::type>
- type;
-};
+using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
template <typename S, typename OutputIt, typename... Args,
FMT_ENABLE_IF(
internal::is_output_iterator<OutputIt>::value &&
!internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
-inline OutputIt vformat_to(
- OutputIt out, const S& format_str,
- typename format_args_t<OutputIt, char_t<S>>::type args) {
- typedef internal::output_range<OutputIt, char_t<S>> range;
+inline OutputIt vformat_to(OutputIt out, const S& format_str,
+ format_args_t<OutputIt, char_t<S>> args) {
+ using range = internal::output_range<OutputIt, char_t<S>>;
return vformat_to<arg_formatter<range>>(range(out),
to_string_view(format_str), args);
}
@@ -3384,17 +3366,16 @@ inline OutputIt vformat_to(
fmt::format_to(std::back_inserter(out), "{}", 42);
\endrst
*/
-template <typename OutputIt, typename S, typename... Args>
-inline OutputIt format_to(OutputIt out, const S& format_str,
- const Args&... args) {
- static_assert(internal::is_output_iterator<OutputIt>::value &&
- internal::is_string<S>::value,
- "");
+template <typename OutputIt, typename S, typename... Args,
+ FMT_ENABLE_IF(
+ internal::is_output_iterator<OutputIt>::value &&
+ !internal::is_contiguous_back_insert_iterator<OutputIt>::value &&
+ internal::is_string<S>::value)>
+inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
- typedef typename format_context_t<OutputIt, char_t<S>>::type context;
- format_arg_store<context, Args...> as{args...};
+ using context = format_context_t<OutputIt, char_t<S>>;
return vformat_to(out, to_string_view(format_str),
- basic_format_args<context>(as));
+ {make_format_args<context>(args...)});
}
template <typename OutputIt> struct format_to_n_result {
@@ -3405,30 +3386,26 @@ template <typename OutputIt> struct format_to_n_result {
};
template <typename OutputIt, typename Char = typename OutputIt::value_type>
-struct format_to_n_context
- : format_context_t<fmt::internal::truncating_iterator<OutputIt>, Char> {};
+using format_to_n_context =
+ format_context_t<internal::truncating_iterator<OutputIt>, Char>;
template <typename OutputIt, typename Char = typename OutputIt::value_type>
-struct format_to_n_args {
- typedef basic_format_args<typename format_to_n_context<OutputIt, Char>::type>
- type;
-};
+using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
template <typename OutputIt, typename Char, typename... Args>
-inline format_arg_store<typename format_to_n_context<OutputIt, Char>::type,
- Args...>
+inline format_arg_store<format_to_n_context<OutputIt, Char>, Args...>
make_format_to_n_args(const Args&... args) {
- return format_arg_store<typename format_to_n_context<OutputIt, Char>::type,
- Args...>(args...);
+ return format_arg_store<format_to_n_context<OutputIt, Char>, Args...>(
+ args...);
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
inline format_to_n_result<OutputIt> vformat_to_n(
OutputIt out, std::size_t n, basic_string_view<Char> format_str,
- typename format_to_n_args<OutputIt, Char>::type args) {
- typedef internal::truncating_iterator<OutputIt> It;
- auto it = vformat_to(It(out, n), format_str, args);
+ format_to_n_args<OutputIt, Char> args) {
+ auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
+ format_str, args);
return {it.base(), it.count()};
}
@@ -3446,11 +3423,9 @@ inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n,
const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
- using Char = char_t<S>;
- format_arg_store<typename format_to_n_context<OutputIt, Char>::type, Args...>
- as(args...);
+ using context = format_to_n_context<OutputIt, char_t<S>>;
return vformat_to_n(out, n, to_string_view(format_str),
- typename format_to_n_args<OutputIt, Char>::type(as));
+ {make_format_args<context>(args...)});
}
template <typename Char>
@@ -3459,7 +3434,7 @@ inline std::basic_string<Char> internal::vformat(
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
- return fmt::to_string(buffer);
+ return to_string(buffer);
}
/**
@@ -3468,8 +3443,22 @@ inline std::basic_string<Char> internal::vformat(
*/
template <typename... Args>
inline std::size_t formatted_size(string_view format_str, const Args&... args) {
- auto it = format_to(internal::counting_iterator<char>(), format_str, args...);
- return it.count();
+ return format_to(internal::counting_iterator(), format_str, args...).count();
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(std::FILE* f, basic_string_view<Char> format_str,
+ wformat_args args) {
+ wmemory_buffer buffer;
+ internal::vformat_to(buffer, format_str, args);
+ buffer.push_back(L'\0');
+ if (std::fputws(buffer.data(), f) == -1)
+ FMT_THROW(system_error(errno, "cannot write to file"));
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(basic_string_view<Char> format_str, wformat_args args) {
+ vprint(stdout, format_str, args);
}
#if FMT_USE_USER_DEFINED_LITERALS
@@ -3479,29 +3468,28 @@ namespace internal {
template <typename Char, Char... CHARS> class udl_formatter {
public:
template <typename... Args>
- std::basic_string<Char> operator()(const Args&... args) const {
+ std::basic_string<Char> operator()(Args&&... args) const {
FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
FMT_CONSTEXPR_DECL bool invalid_format =
- do_check_format_string<Char, error_handler, Args...>(
+ do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
basic_string_view<Char>(s, sizeof...(CHARS)));
(void)invalid_format;
- return format(s, args...);
+ return format(s, std::forward<Args>(args)...);
}
};
# else
template <typename Char> struct udl_formatter {
- const Char* str;
+ basic_string_view<Char> str;
template <typename... Args>
- auto operator()(Args&&... args) const
- -> decltype(format(str, std::forward<Args>(args)...)) {
+ std::basic_string<Char> operator()(Args&&... args) const {
return format(str, std::forward<Args>(args)...);
}
};
# endif // FMT_USE_UDL_TEMPLATE
template <typename Char> struct udl_arg {
- const Char* str;
+ basic_string_view<Char> str;
template <typename T> named_arg<T, Char> operator=(T&& value) const {
return {str, std::forward<T>(value)};
@@ -3532,13 +3520,13 @@ FMT_CONSTEXPR internal::udl_formatter<Char, CHARS...> operator""_format() {
std::string message = "The answer is {}"_format(42);
\endrst
*/
-inline internal::udl_formatter<char> operator"" _format(const char* s,
- std::size_t) {
- return {s};
+FMT_CONSTEXPR internal::udl_formatter<char> operator"" _format(const char* s,
+ std::size_t n) {
+ return {{s, n}};
}
-inline internal::udl_formatter<wchar_t> operator"" _format(const wchar_t* s,
- std::size_t) {
- return {s};
+FMT_CONSTEXPR internal::udl_formatter<wchar_t> operator"" _format(
+ const wchar_t* s, std::size_t n) {
+ return {{s, n}};
}
# endif // FMT_USE_UDL_TEMPLATE
@@ -3552,16 +3540,34 @@ inline internal::udl_formatter<wchar_t> operator"" _format(const wchar_t* s,
fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
\endrst
*/
-inline internal::udl_arg<char> operator"" _a(const char* s, std::size_t) {
- return {s};
+FMT_CONSTEXPR internal::udl_arg<char> operator"" _a(const char* s,
+ std::size_t n) {
+ return {{s, n}};
}
-inline internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s, std::size_t) {
- return {s};
+FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
+ std::size_t n) {
+ return {{s, n}};
}
} // namespace literals
#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<std::remove_pointer< \
+ typename std::decay<decltype(s)>::type>::type>::type; \
+ __VA_ARGS__ FMT_CONSTEXPR \
+ operator fmt::basic_string_view<char_type>() const { \
+ return {s, sizeof(s) / sizeof(char_type) - 1}; \
+ } \
+ } result; \
+ /* Suppress Qt Creator warning about unused operator. */ \
+ (void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
+ result); \
+ return result; \
+ }()
+
/**
\rst
Constructs a compile-time format string.
@@ -3572,37 +3578,10 @@ FMT_END_NAMESPACE
std::string s = format(FMT_STRING("{:d}"), "foo");
\endrst
*/
-#define FMT_STRING(s) \
- [] { \
- struct str : fmt::compile_string { \
- typedef typename std::remove_cv<std::remove_pointer< \
- typename std::decay<decltype(s)>::type>::type>::type char_type; \
- FMT_CONSTEXPR operator fmt::basic_string_view<char_type>() const { \
- return {s, sizeof(s) / sizeof(char_type) - 1}; \
- } \
- } result; \
- /* Suppress Qt Creator warning about unused operator. */ \
- (void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
- result); \
- return result; \
- }()
+#define FMT_STRING(s) FMT_STRING_IMPL(s, )
#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
-/**
- \rst
- Constructs a compile-time format string. This macro is disabled by default to
- prevent potential name collisions. To enable it define ``FMT_STRING_ALIAS`` to
- 1 before including ``fmt/format.h``.
-
- **Example**::
-
- #define FMT_STRING_ALIAS 1
- #include <fmt/format.h>
- // A compile-time error because 'd' is an invalid specifier for strings.
- std::string s = format(fmt("{:d}"), "foo");
- \endrst
- */
-# define fmt(s) FMT_STRING(s)
+# define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
#endif
#ifdef FMT_HEADER_ONLY
diff --git a/src/third_party/fmt/dist/include/fmt/locale.h b/src/third_party/fmt/dist/include/fmt/locale.h
index 1faf9723429..7c13656e4fa 100644
--- a/src/third_party/fmt/dist/include/fmt/locale.h
+++ b/src/third_party/fmt/dist/include/fmt/locale.h
@@ -43,10 +43,10 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
- const S& format_str,
- const Args&... args) {
- return internal::vformat(loc, to_string_view(format_str),
- {internal::make_args_checked(format_str, args...)});
+ const S& format_str, Args&&... args) {
+ return internal::vformat(
+ loc, to_string_view(format_str),
+ {internal::make_args_checked<Args...>(format_str, args...)});
}
template <typename S, typename OutputIt, typename... Args,
@@ -54,7 +54,7 @@ template <typename S, typename OutputIt, typename... Args,
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
- typename format_args_t<OutputIt, Char>::type args) {
+ format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
@@ -64,9 +64,9 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
- const S& format_str, const Args&... args) {
+ const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
- using context = typename format_context_t<OutputIt, char_t<S>>::type;
+ using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
diff --git a/src/third_party/fmt/dist/include/fmt/ostream.h b/src/third_party/fmt/dist/include/fmt/ostream.h
index 49aa58054c6..72d078b20c6 100644
--- a/src/third_party/fmt/dist/include/fmt/ostream.h
+++ b/src/third_party/fmt/dist/include/fmt/ostream.h
@@ -16,8 +16,8 @@ namespace internal {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
- typedef typename std::basic_streambuf<Char>::int_type int_type;
- typedef typename std::basic_streambuf<Char>::traits_type traits_type;
+ using int_type = typename std::basic_streambuf<Char>::int_type;
+ using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
@@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
- struct null;
// Hide all operator<< from std::basic_ostream<Char>.
- void operator<<(null);
+ void_t<> operator<<(null<>);
+ void_t<> operator<<(const Char*);
+
+ template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
+ !std::is_enum<T>::value)>
+ void_t<> operator<<(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
@@ -56,14 +60,14 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
- static decltype((void)(std::declval<test_stream<Char>&>()
- << std::declval<U>()),
- std::true_type())
+ static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
+ << std::declval<U>()),
+ void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
- typedef decltype(test<T>(0)) result;
+ using result = decltype(test<T>(0));
public:
static const bool value = result::value;
@@ -73,12 +77,11 @@ template <typename T, typename Char> class is_streamable {
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
- typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
- UnsignedStreamSize size = buf.size();
- UnsignedStreamSize max_size =
- internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
+ using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
+ unsigned_streamsize size = buf.size();
+ unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
- UnsignedStreamSize n = size <= max_size ? size : max_size;
+ unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
@@ -86,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
-void format_value(buffer<Char>& buf, const T& value) {
- internal::formatbuf<Char> format_buf(buf);
+void format_value(buffer<Char>& buf, const T& value,
+ locale_ref loc = locale_ref()) {
+ formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
+ if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
@@ -96,13 +101,12 @@ void format_value(buffer<Char>& buf, const T& value) {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
-struct fallback_formatter<T, Char,
- enable_if_t<internal::is_streamable<T, Char>::value>>
+struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
- internal::format_value(buffer, value);
+ format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
@@ -128,10 +132,9 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
-inline void print(std::basic_ostream<Char>& os, const S& format_str,
- const Args&... args) {
+void print(std::basic_ostream<Char>& 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<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE
diff --git a/src/third_party/fmt/dist/include/fmt/posix.h b/src/third_party/fmt/dist/include/fmt/posix.h
index e5394f40051..e68e226f3f1 100644
--- a/src/third_party/fmt/dist/include/fmt/posix.h
+++ b/src/third_party/fmt/dist/include/fmt/posix.h
@@ -13,11 +13,10 @@
# undef __STRICT_ANSI__
#endif
-#include <errno.h>
-#include <fcntl.h> // for O_RDONLY
-#include <locale.h> // for locale_t
-#include <stdio.h>
-#include <stdlib.h> // for strtod_l
+#include <cerrno>
+#include <clocale> // for locale_t
+#include <cstdio>
+#include <cstdlib> // for strtod_l
#include <cstddef>
@@ -27,6 +26,18 @@
#include "format.h"
+// UWP doesn't provide _pipe.
+#if FMT_HAS_INCLUDE("winapifamily.h")
+# include <winapifamily.h>
+#endif
+#if FMT_HAS_INCLUDE("fcntl.h") && \
+ (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+# include <fcntl.h> // for O_RDONLY
+# define FMT_USE_FCNTL 1
+#else
+# define FMT_USE_FCNTL 0
+#endif
+
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
@@ -54,8 +65,8 @@
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
- result = (expression); \
- } while (result == error_result && errno == EINTR)
+ (result) = (expression); \
+ } while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
@@ -69,7 +80,7 @@ FMT_BEGIN_NAMESPACE
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 typedefs for common character types:
+ You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
@@ -108,8 +119,8 @@ template <typename Char> class basic_cstring_view {
const Char* c_str() const { return data_; }
};
-typedef basic_cstring_view<char> cstring_view;
-typedef basic_cstring_view<wchar_t> wcstring_view;
+using cstring_view = basic_cstring_view<char>;
+using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
@@ -132,16 +143,15 @@ class buffered_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;
- private:
- buffered_file(const buffered_file&) = delete;
- void operator=(const buffered_file&) = delete;
-
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
@@ -177,6 +187,7 @@ class buffered_file {
}
};
+#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
@@ -204,14 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
- private:
+ public:
file(const file&) = delete;
void operator=(const file&) = delete;
- public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
- file& operator=(file&& other) {
+ file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -260,13 +270,14 @@ class file {
// Returns the memory page size.
long getpagesize();
+#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
-# ifdef _MSC_VER
- typedef _locale_t locale_t;
+# ifdef _WIN32
+ using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
@@ -283,18 +294,17 @@ class Locale {
locale_t locale_;
+ public:
+ using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
- public:
- typedef locale_t Type;
-
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_; }
+ type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
diff --git a/src/third_party/fmt/dist/include/fmt/printf.h b/src/third_party/fmt/dist/include/fmt/printf.h
index c2e9f9bf1af..cdbb65e6a5d 100644
--- a/src/third_party/fmt/dist/include/fmt/printf.h
+++ b/src/third_party/fmt/dist/include/fmt/printf.h
@@ -1,4 +1,4 @@
-// Formatting library for C++
+// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
-#include <algorithm> // std::fill_n
+#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
@@ -16,15 +16,11 @@
FMT_BEGIN_NAMESPACE
namespace internal {
-// A helper function to suppress bogus "conditional expression is constant"
-// warnings.
-template <typename T> inline T const_check(T value) { return value; }
-
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
- unsigned max = std::numeric_limits<int>::max();
+ unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
@@ -33,7 +29,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
- value <= std::numeric_limits<int>::max();
+ value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
@@ -70,17 +66,17 @@ class is_zero_int {
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
-template <> struct make_unsigned_or_bool<bool> { typedef bool type; };
+template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <typename T, typename Context> class arg_converter {
private:
- typedef typename Context::char_type Char;
+ using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
- typename Context::char_type type_;
+ char_type type_;
public:
- arg_converter(basic_format_arg<Context>& arg, Char type)
+ arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
@@ -97,9 +93,9 @@ template <typename T, typename Context> class arg_converter {
arg_ = internal::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
- typedef typename make_unsigned_or_bool<target_type>::type Unsigned;
+ using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
- static_cast<unsigned>(static_cast<Unsigned>(value)));
+ static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
if (is_signed) {
@@ -137,8 +133,8 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
- typedef typename Context::char_type Char;
- arg_ = internal::make_arg<Context>(static_cast<Char>(value));
+ arg_ = internal::make_arg<Context>(
+ static_cast<typename Context::char_type>(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -149,22 +145,21 @@ template <typename Context> class char_converter {
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
private:
- typedef basic_format_specs<Char> format_specs;
+ using format_specs = basic_format_specs<Char>;
- format_specs& spec_;
+ format_specs& specs_;
public:
- explicit printf_width_handler(format_specs& spec) : spec_(spec) {}
+ explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
- typedef typename internal::int_traits<T>::main_type UnsignedType;
- UnsignedType width = static_cast<UnsignedType>(value);
+ auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
- spec_.align_ = ALIGN_LEFT;
+ specs_.align = align::left;
width = 0 - width;
}
- unsigned int_max = std::numeric_limits<int>::max();
+ unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
@@ -204,53 +199,54 @@ template <typename OutputIt, typename Char> class basic_printf_context;
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
public:
- typedef decltype(std::declval<Range>().begin()) iterator;
+ using iterator = typename Range::iterator;
private:
- typedef typename Range::value_type char_type;
- typedef internal::arg_formatter_base<Range> base;
- typedef basic_printf_context<iterator, char_type> context_type;
+ using char_type = typename Range::value_type;
+ using base = internal::arg_formatter_base<Range>;
+ using context_type = basic_printf_context<iterator, char_type>;
context_type& context_;
void write_null_pointer(char) {
- this->spec()->type = 0;
+ this->specs()->type = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
- this->spec()->type = 0;
+ this->specs()->type = 0;
this->write(L"(nil)");
}
public:
- typedef typename base::format_specs format_specs;
+ using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
- *buffer* is a reference to the output buffer and *spec* contains format
+ *buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
- printf_arg_formatter(iterator iter, format_specs& spec, context_type& ctx)
- : base(Range(iter), &spec, internal::locale_ref()), context_(ctx) {}
+ printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
+ : base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
- template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+ template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
- format_specs& fmt_spec = *this->spec();
- if (fmt_spec.type != 's') return base::operator()(value ? 1 : 0);
- fmt_spec.type = 0;
+ format_specs& fmt_specs = *this->specs();
+ if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
+ fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
- format_specs& fmt_spec = *this->spec();
- if (fmt_spec.type && fmt_spec.type != 'c')
+ format_specs& fmt_specs = *this->specs();
+ if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
- fmt_spec.flags = 0;
- fmt_spec.align_ = ALIGN_RIGHT;
+ fmt_specs.sign = sign::none;
+ fmt_specs.alt = false;
+ fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
@@ -267,7 +263,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
iterator operator()(const char* value) {
if (value)
base::operator()(value);
- else if (this->spec()->type == 'p')
+ else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
@@ -278,7 +274,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
iterator operator()(const wchar_t* value) {
if (value)
base::operator()(value);
- else if (this->spec()->type == 'p')
+ else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
@@ -294,7 +290,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
- this->spec()->type = 0;
+ this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
}
@@ -328,20 +324,21 @@ template <typename OutputIt, typename Char> class basic_printf_context {
template <typename T> using formatter_type = printf_formatter<T>;
private:
- typedef basic_format_specs<char_type> format_specs;
+ using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
- basic_parse_context<Char> parse_ctx_;
+ basic_format_parse_context<Char> parse_ctx_;
- static void parse_flags(format_specs& spec, const Char*& it, const Char* end);
+ static void parse_flags(format_specs& specs, const Char*& it,
+ const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
- format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
+ format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Parses argument index, flags and width and returns the argument index.
- unsigned parse_header(const Char*& it, const Char* end, format_specs& spec);
+ unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
@@ -360,38 +357,37 @@ template <typename OutputIt, typename Char> class basic_printf_context {
format_arg arg(unsigned id) const { return args_.get(id); }
- basic_parse_context<Char>& parse_context() { return parse_ctx_; }
+ basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
- template <typename ArgFormatter =
- printf_arg_formatter<internal::buffer_range<Char>>>
+ template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format();
};
template <typename OutputIt, typename Char>
-void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& spec,
+void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
- spec.align_ = ALIGN_LEFT;
+ specs.align = align::left;
break;
case '+':
- spec.flags |= SIGN_FLAG | PLUS_FLAG;
+ specs.sign = sign::plus;
break;
case '0':
- spec.fill_ = '0';
+ specs.fill[0] = '0';
break;
case ' ':
- spec.flags |= SIGN_FLAG;
+ specs.sign = sign::space;
break;
case '#':
- spec.flags |= HASH_FLAG;
+ specs.alt = true;
break;
default:
return;
@@ -402,7 +398,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& spec,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
- if (arg_index == std::numeric_limits<unsigned>::max())
+ if (arg_index == internal::max_value<unsigned>())
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
@@ -411,8 +407,8 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
- const Char*& it, const Char* end, format_specs& spec) {
- unsigned arg_index = std::numeric_limits<unsigned>::max();
+ const Char*& it, const Char* end, format_specs& specs) {
+ unsigned arg_index = internal::max_value<unsigned>();
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
@@ -423,25 +419,25 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
++it;
arg_index = value;
} else {
- if (c == '0') spec.fill_ = '0';
+ if (c == '0') specs.fill[0] = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
- spec.width_ = value;
+ specs.width = value;
return arg_index;
}
}
}
- parse_flags(spec, it, end);
+ parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
- spec.width_ = parse_nonnegative_int(it, end, eh);
+ specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
- spec.width_ = visit_format_arg(
- internal::printf_width_handler<char_type>(spec), get_arg());
+ specs.width = visit_format_arg(
+ internal::printf_width_handler<char_type>(specs), get_arg());
}
}
return arg_index;
@@ -464,11 +460,12 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
}
out = std::copy(start, it - 1, out);
- format_specs spec;
- spec.align_ = ALIGN_RIGHT;
+ format_specs specs;
+ specs.align = align::right;
// Parse argument index, flags and width.
- unsigned arg_index = parse_header(it, end, spec);
+ unsigned arg_index = parse_header(it, end, specs);
+ if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
if (it != end && *it == '.') {
@@ -476,25 +473,24 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
- spec.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
+ specs.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
} else if (c == '*') {
++it;
- spec.precision =
+ specs.precision =
visit_format_arg(internal::printf_precision_handler(), get_arg());
} else {
- spec.precision = 0;
+ specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
- if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
- spec.flags = static_cast<uint_least8_t>(
- spec.flags & (~internal::to_unsigned<int>(HASH_FLAG)));
- if (spec.fill_ == '0') {
+ if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
+ specs.alt = false;
+ if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
- spec.align_ = ALIGN_NUMERIC;
+ specs.align = align::numeric;
else
- spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
+ specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
@@ -540,13 +536,13 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
- spec.type = static_cast<char>(*it++);
+ specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
- switch (spec.type) {
+ switch (specs.type) {
case 'i':
case 'u':
- spec.type = 'd';
+ specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
@@ -558,22 +554,21 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
- visit_format_arg(ArgFormatter(out, spec, *this), arg);
+ visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
-template <typename Buffer> struct basic_printf_context_t {
- typedef basic_printf_context<std::back_insert_iterator<Buffer>,
- typename Buffer::value_type>
- type;
-};
+template <typename Char>
+using basic_printf_context_t =
+ basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
+ Char>;
-typedef basic_printf_context_t<internal::buffer<char>>::type printf_context;
-typedef basic_printf_context_t<internal::buffer<wchar_t>>::type wprintf_context;
+using printf_context = basic_printf_context_t<char>;
+using wprintf_context = basic_printf_context_t<wchar_t>;
-typedef basic_format_args<printf_context> printf_args;
-typedef basic_format_args<wprintf_context> wprintf_args;
+using printf_args = basic_format_args<printf_context>;
+using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
@@ -601,10 +596,7 @@ inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
- const S& format,
- basic_format_args<
- typename basic_printf_context_t<internal::buffer<Char>>::type>
- args) {
+ const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer);
@@ -622,17 +614,13 @@ inline std::basic_string<Char> vsprintf(
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
- using context = typename basic_printf_context_t<internal::buffer<Char>>::type;
- format_arg_store<context, Args...> as{args...};
- return vsprintf(to_string_view(format), basic_format_args<context>(as));
+ using context = basic_printf_context_t<Char>;
+ return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
-inline int vfprintf(
- std::FILE* f, const S& format,
- basic_format_args<
- typename basic_printf_context_t<internal::buffer<Char>>::type>
- args) {
+inline int vfprintf(std::FILE* f, const S& format,
+ basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
@@ -653,17 +641,14 @@ inline int vfprintf(
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
- using context = typename basic_printf_context_t<internal::buffer<Char>>::type;
- format_arg_store<context, Args...> as{args...};
- return vfprintf(f, to_string_view(format), basic_format_args<context>(as));
+ using context = basic_printf_context_t<Char>;
+ return vfprintf(f, to_string_view(format),
+ {make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
-inline int vprintf(
- const S& format,
- basic_format_args<
- typename basic_printf_context_t<internal::buffer<Char>>::type>
- args) {
+inline int vprintf(const S& format,
+ basic_format_args<basic_printf_context_t<Char>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
@@ -679,18 +664,14 @@ inline int vprintf(
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
- using buffer = internal::buffer<char_t<S>>;
- using context = typename basic_printf_context_t<buffer>::type;
- format_arg_store<context, Args...> as{args...};
- return vprintf(to_string_view(format_str), basic_format_args<context>(as));
+ using context = basic_printf_context_t<char_t<S>>;
+ return vprintf(to_string_view(format_str),
+ {make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
-inline int vfprintf(
- std::basic_ostream<Char>& os, const S& format,
- basic_format_args<
- typename basic_printf_context_t<internal::buffer<Char>>::type>
- args) {
+inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
+ basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
@@ -721,10 +702,9 @@ typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
- using context = typename basic_printf_context_t<internal::buffer<Char>>::type;
- format_arg_store<context, Args...> as{args...};
+ using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
- basic_format_args<context>(as));
+ {make_format_args<context>(args...)});
}
FMT_END_NAMESPACE
diff --git a/src/third_party/fmt/dist/include/fmt/ranges.h b/src/third_party/fmt/dist/include/fmt/ranges.h
index 9128f25a195..6110fdaf194 100644
--- a/src/third_party/fmt/dist/include/fmt/ranges.h
+++ b/src/third_party/fmt/dist/include/fmt/ranges.h
@@ -124,7 +124,7 @@ template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
- typedef T value_type;
+ using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
};
@@ -242,13 +242,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
}
};
-template <typename T> struct is_range {
+template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
- internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
+ internal::is_range_<T>::value &&
+ !internal::is_like_std_string<T>::value &&
+ !std::is_convertible<T, std::basic_string<Char>>::value &&
+ !std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
-struct formatter<RangeT, Char, enable_if_t<fmt::is_range<RangeT>::value>> {
+struct formatter<RangeT, Char,
+ enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting;
template <typename ParseContext>
@@ -280,6 +284,82 @@ struct formatter<RangeT, Char, enable_if_t<fmt::is_range<RangeT>::value>> {
}
};
+template <typename Char, typename... T> struct tuple_arg_join : internal::view {
+ const std::tuple<T...>& tuple;
+ basic_string_view<Char> sep;
+
+ tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
+ : tuple{t}, sep{s} {}
+};
+
+template <typename Char, typename... T>
+struct formatter<tuple_arg_join<Char, T...>, Char> {
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ return ctx.begin();
+ }
+
+ template <typename FormatContext>
+ typename FormatContext::iterator format(
+ const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
+ return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
+ }
+
+ private:
+ template <typename FormatContext, size_t... N>
+ typename FormatContext::iterator format(
+ const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
+ internal::index_sequence<N...>) {
+ return format_args(value, ctx, std::get<N>(value.tuple)...);
+ }
+
+ template <typename FormatContext>
+ typename FormatContext::iterator format_args(
+ const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
+ // NOTE: for compilers that support C++17, this empty function instantiation
+ // can be replaced with a constexpr branch in the variadic overload.
+ return ctx.out();
+ }
+
+ template <typename FormatContext, typename Arg, typename... Args>
+ typename FormatContext::iterator format_args(
+ const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
+ const Arg& arg, const Args&... args) {
+ using base = formatter<typename std::decay<Arg>::type, Char>;
+ auto out = ctx.out();
+ out = base{}.format(arg, ctx);
+ if (sizeof...(Args) > 0) {
+ out = std::copy(value.sep.begin(), value.sep.end(), out);
+ ctx.advance_to(out);
+ return format_args(value, ctx, args...);
+ }
+ return out;
+ }
+};
+
+/**
+ \rst
+ Returns an object that formats `tuple` with elements separated by `sep`.
+
+ **Example**::
+
+ std::tuple<int, char> t = {1, 'a'};
+ fmt::print("{}", fmt::join(t, ", "));
+ // Output: "1, a"
+ \endrst
+ */
+template <typename... T>
+FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
+ string_view sep) {
+ return {tuple, sep};
+}
+
+template <typename... T>
+FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
+ wstring_view sep) {
+ return {tuple, sep};
+}
+
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
diff --git a/src/third_party/fmt/dist/src/format.cc b/src/third_party/fmt/dist/src/format.cc
index b60b7e4fa0e..c373db1c9df 100644
--- a/src/third_party/fmt/dist/src/format.cc
+++ b/src/third_party/fmt/dist/src/format.cc
@@ -10,9 +10,10 @@
FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>;
-// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
-bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned,
- int&) = internal::grisu_format;
+// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
+int (*instantiate_format_float)(double, int, internal::float_specs,
+ internal::buffer<char>&) =
+ internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
@@ -21,7 +22,9 @@ template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char.
+template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
+template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
@@ -34,22 +37,27 @@ template FMT_API std::string internal::vformat<char>(
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
-template FMT_API void internal::sprintf_format(double, internal::buffer<char>&,
- core_format_specs);
-template FMT_API void internal::sprintf_format(long double,
- internal::buffer<char>&,
- core_format_specs);
+template FMT_API int internal::snprintf_float(double, int,
+ internal::float_specs,
+ internal::buffer<char>&);
+template FMT_API int internal::snprintf_float(long double, int,
+ internal::float_specs,
+ internal::buffer<char>&);
+template FMT_API int internal::format_float(double, int, internal::float_specs,
+ internal::buffer<char>&);
+template FMT_API int internal::format_float(long double, int,
+ internal::float_specs,
+ internal::buffer<char>&);
// Explicit instantiations for wchar_t.
+template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
+template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
-template FMT_API void internal::arg_map<wformat_context>::init(
- const basic_format_args<wformat_context>&);
-
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE
diff --git a/src/third_party/fmt/dist/src/posix.cc b/src/third_party/fmt/dist/src/posix.cc
index 69c27819d2b..3db72b76e25 100644
--- a/src/third_party/fmt/dist/src/posix.cc
+++ b/src/third_party/fmt/dist/src/posix.cc
@@ -12,7 +12,9 @@
#include "fmt/posix.h"
-#include <limits.h>
+#include <climits>
+
+#if FMT_USE_FCNTL
#include <sys/stat.h>
#include <sys/types.h>
@@ -39,8 +41,8 @@
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
-
#endif // _WIN32
+#endif // FMT_USE_FCNTL
#ifdef fileno
# undef fileno
@@ -49,7 +51,7 @@
namespace {
#ifdef _WIN32
// Return type of read and write functions.
-typedef int RWResult;
+using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
@@ -58,7 +60,7 @@ inline unsigned convert_rwcount(std::size_t count) {
}
#else
// Return type of read and write functions.
-typedef ssize_t RWResult;
+using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
@@ -94,6 +96,7 @@ int buffered_file::fileno() const {
return fd;
}
+#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
@@ -138,7 +141,7 @@ long long file::size() const {
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
- typedef struct stat Stat;
+ using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
@@ -230,4 +233,5 @@ long getpagesize() {
return size;
#endif
}
+#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE
diff --git a/src/third_party/fmt/scripts/import.sh b/src/third_party/fmt/scripts/import.sh
index dc17b20beec..309415ef31a 100755
--- a/src/third_party/fmt/scripts/import.sh
+++ b/src/third_party/fmt/scripts/import.sh
@@ -4,7 +4,7 @@
set -vxeuo pipefail
FMT_GIT_URL="https://github.com/mongodb-forks/fmt.git"
-FMT_GIT_REV=e37ee419c6c31825ee56b06590dec0b2561ab310 # 2019-06-24
+FMT_GIT_REV=6.1.1
FMT_GIT_DIR=$(mktemp -d /tmp/import-fmt.XXXXXX)
trap "rm -rf $FMT_GIT_DIR" EXIT