summaryrefslogtreecommitdiff
path: root/src/third_party/fmt/dist/include/fmt/format-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/fmt/dist/include/fmt/format-inl.h')
-rw-r--r--src/third_party/fmt/dist/include/fmt/format-inl.h231
1 files changed, 136 insertions, 95 deletions
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 ad82d328679..cce1a34a426 100644
--- a/src/third_party/fmt/dist/include/fmt/format-inl.h
+++ b/src/third_party/fmt/dist/include/fmt/format-inl.h
@@ -63,8 +63,7 @@ inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) {
}
FMT_BEGIN_NAMESPACE
-
-namespace {
+namespace internal {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
@@ -79,13 +78,7 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
-#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
-# define FMT_SWPRINTF snwprintf
-#else
-# define FMT_SWPRINTF swprintf
-#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
-
-typedef void (*FormatFunc)(internal::buffer&, int, string_view);
+typedef void (*FormatFunc)(internal::buffer<char>&, int, string_view);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
@@ -96,9 +89,9 @@ typedef void (*FormatFunc)(internal::buffer&, int, string_view);
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
-int safe_strerror(int error_code, char*& buffer,
- std::size_t buffer_size) FMT_NOEXCEPT {
- FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
+FMT_FUNC int safe_strerror(int error_code, char*& buffer,
+ std::size_t buffer_size) FMT_NOEXCEPT {
+ FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
class dispatcher {
private:
@@ -154,8 +147,8 @@ int safe_strerror(int error_code, char*& buffer,
return dispatcher(error_code, buffer, buffer_size).run();
}
-void format_error_code(internal::buffer& out, int error_code,
- string_view message) FMT_NOEXCEPT {
+FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
+ string_view message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential
// bad_alloc.
@@ -182,15 +175,16 @@ void format_error_code(internal::buffer& out, int error_code,
}
// try an fwrite, FMT_THROW on failure
-void fwrite_fully(const void* ptr, size_t size, size_t count, FILE* stream) {
+FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
+ FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) {
FMT_THROW(system_error(errno, "cannot write to file"));
}
}
-void report_error(FormatFunc func, int error_code,
- string_view message) FMT_NOEXCEPT {
+FMT_FUNC void report_error(FormatFunc 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
@@ -198,16 +192,7 @@ void report_error(FormatFunc func, int error_code,
fwrite_fully(full_message.data(), 1, full_message.size(), stderr);
std::fputc('\n', stderr);
}
-} // namespace
-
-FMT_FUNC size_t internal::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;
-}
+} // namespace internal
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
namespace internal {
@@ -223,7 +208,7 @@ template <typename Locale> Locale locale_ref::get() const {
}
template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
- return std::use_facet<std::numpunct<Char> >(loc.get<std::locale>())
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.thousands_sep();
}
} // namespace internal
@@ -234,6 +219,9 @@ FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
}
#endif
+FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {}
+FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT {}
+
FMT_FUNC void system_error::init(int err_code, string_view format_str,
format_args args) {
error_code_ = err_code;
@@ -244,20 +232,22 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
}
namespace internal {
-template <typename T>
-int char_traits<char>::format_float(char* buf, std::size_t size,
- const char* format, int precision,
- T value) {
- return precision < 0 ? FMT_SNPRINTF(buf, size, format, value)
- : FMT_SNPRINTF(buf, size, format, precision, value);
+
+template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
+ // Assume little endian; pointer formatting is implementation-defined anyway.
+ 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;
+ return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
}
template <typename T>
-int char_traits<wchar_t>::format_float(wchar_t* buf, std::size_t size,
- const wchar_t* format, int precision,
- T value) {
- return precision < 0 ? FMT_SWPRINTF(buf, size, format, value)
- : FMT_SWPRINTF(buf, size, format, precision, value);
+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>
@@ -268,6 +258,9 @@ const char basic_data<T>::DIGITS[] =
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
+template <typename T>
+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, \
@@ -428,6 +421,13 @@ inline fp operator-(fp x, fp y) {
// 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;
+#if FMT_USE_INT128
+ auto product = static_cast<__uint128_t>(x.f) * y.f;
+ auto f = static_cast<uint64_t>(product >> 64);
+ if ((static_cast<uint64_t>(product) & (1ULL << 63)) != 0) ++f;
+ return fp(f, exp);
+#else
// Multiply 32-bit parts of significands.
uint64_t mask = (1ULL << 32) - 1;
uint64_t a = x.f >> 32, b = x.f & mask;
@@ -435,7 +435,8 @@ FMT_FUNC fp operator*(fp x, fp y) {
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), x.e + y.e + 64);
+ return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), exp);
+#endif
}
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
@@ -483,10 +484,12 @@ enum result {
};
}
-// Generates output using Grisu2 digit-gen algorithm.
+// Generates output using the Grisu digit-gen algorithm.
+// error: the size of the region (lower, upper) outside of which numbers
+// definitely do not round to value (Delta in Grisu3).
template <typename Handler>
-digits::result grisu2_gen_digits(fp value, uint64_t error, int& exp,
- Handler& handler) {
+digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
+ Handler& handler) {
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
@@ -599,7 +602,7 @@ struct fixed_handler {
uint64_t error, int, bool integral) {
FMT_ASSERT(remainder < divisor, "");
buf[size++] = digit;
- if (size != precision) return digits::more;
+ if (size < precision) return digits::more;
if (!integral) {
// Check if error * 2 < divisor with overflow prevention.
// The check is not needed for the integral part because error = 1
@@ -624,34 +627,58 @@ struct fixed_handler {
};
// The shortest representation digit handler.
-struct shortest_handler {
+template <int GRISU_VERSION> struct grisu_shortest_handler {
char* buf;
int size;
- fp diff; // wp_w in Grisu.
+ // Distance between scaled value and upper bound (wp_W in Grisu3).
+ uint64_t diff;
digits::result on_start(uint64_t, uint64_t, uint64_t, int&) {
return digits::more;
}
- digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
- uint64_t error, int exp, bool integral) {
- buf[size++] = digit;
- if (remainder > error) return digits::more;
- uint64_t d = integral ? diff.f : diff.f * data::POWERS_OF_10_64[-exp];
+
+ // Decrement the generated number approaching value from above.
+ void round(uint64_t d, uint64_t divisor, uint64_t& remainder,
+ uint64_t error) {
while (
remainder < d && error - remainder >= divisor &&
- (remainder + divisor < d || d - remainder > remainder + divisor - d)) {
+ (remainder + divisor < d || d - remainder >= remainder + divisor - d)) {
--buf[size - 1];
remainder += divisor;
}
- return digits::done;
+ }
+
+ // Implements Grisu's round_weed.
+ digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
+ 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 up = (diff - 1) * unit; // wp_Wup
+ round(up, divisor, remainder, error);
+ uint64_t down = (diff + 1) * unit; // wp_Wdown
+ if (remainder < down && error - remainder >= divisor &&
+ (remainder + divisor < down ||
+ down - remainder > remainder + divisor - down)) {
+ return digits::error;
+ }
+ return 2 * unit <= remainder && remainder <= error - 4 * unit
+ ? digits::done
+ : digits::error;
}
};
-template <typename Double, typename std::enable_if<
- sizeof(Double) == sizeof(uint64_t), int>::type>
-FMT_FUNC bool grisu2_format(Double value, buffer& buf, int precision,
- bool fixed, int& exp) {
+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) {
FMT_ASSERT(value >= 0, "value is negative");
+ bool fixed = (options & grisu_options::fixed) != 0;
if (value <= 0) { // <= instead of == to silence a warning.
if (precision < 0 || !fixed) {
exp = 0;
@@ -674,7 +701,7 @@ FMT_FUNC bool grisu2_format(Double value, buffer& buf, int precision,
min_exp - (fp_value.e + fp::significand_size), cached_exp10);
fp_value = fp_value * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
- if (grisu2_gen_digits(fp_value, 1, exp, handler) == digits::error)
+ if (grisu_gen_digits(fp_value, 1, exp, handler) == digits::error)
return false;
buf.resize(to_unsigned(handler.size));
} else {
@@ -684,24 +711,36 @@ FMT_FUNC bool grisu2_format(Double value, buffer& buf, int precision,
// 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);
- upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
- --upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
- assert(min_exp <= upper.e && upper.e <= -32);
fp_value.normalize();
fp_value = fp_value * cached_pow;
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
- ++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
- shortest_handler handler{buf.data(), 0, upper - fp_value};
- auto result = grisu2_gen_digits(upper, upper.f - lower.f, exp, handler);
+ 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;
+ }
if (result == digits::error) return false;
- buf.resize(to_unsigned(handler.size));
+ buf.resize(to_unsigned(size));
}
exp -= cached_exp10;
return true;
}
template <typename Double>
-void sprintf_format(Double value, internal::buffer& buf,
+void sprintf_format(Double value, internal::buffer<char>& buf,
core_format_specs spec) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
@@ -734,32 +773,33 @@ void sprintf_format(Double value, internal::buffer& buf,
*format_ptr = '\0';
// Format using snprintf.
- char* start = FMT_NULL;
+ char* start = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
- int result = internal::char_traits<char>::format_float(
- start, buffer_size, format, spec.precision, value);
+ 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') p += 2; // Skip "0x".
- 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);
+ 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);
+ }
}
}
}
@@ -791,7 +831,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
}
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(),
- s_size, FMT_NULL, 0);
+ s_size, nullptr, 0);
if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size,
@@ -817,12 +857,12 @@ FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
return 0;
}
- int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0,
- FMT_NULL, FMT_NULL);
+ int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
+ nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
- length, FMT_NULL, FMT_NULL);
+ length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
@@ -837,7 +877,7 @@ FMT_FUNC void windows_error::init(int err_code, string_view format_str,
base = std::runtime_error(to_string(buffer));
}
-FMT_FUNC void internal::format_windows_error(internal::buffer& out,
+FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
@@ -846,9 +886,9 @@ FMT_FUNC void internal::format_windows_error(internal::buffer& out,
for (;;) {
wchar_t* system_message = &buf[0];
int result = FormatMessageW(
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FMT_NULL,
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
- static_cast<uint32_t>(buf.size()), FMT_NULL);
+ static_cast<uint32_t>(buf.size()), nullptr);
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
@@ -871,14 +911,15 @@ FMT_FUNC void internal::format_windows_error(internal::buffer& out,
#endif // FMT_USE_WINDOWS_H
-FMT_FUNC void format_system_error(internal::buffer& out, int error_code,
+FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
memory_buffer buf;
buf.resize(inline_buffer_size);
for (;;) {
char* system_message = &buf[0];
- int result = safe_strerror(error_code, system_message, buf.size());
+ int result =
+ internal::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
writer w(out);
w.write(message);
@@ -914,14 +955,14 @@ FMT_FUNC void report_windows_error(int error_code,
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
- basic_format_args<buffer_context<char>::type>(args));
- fwrite_fully(buffer.data(), 1, buffer.size(), f);
+ basic_format_args<buffer_context<char>>(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);
- fwrite_fully(buffer.data(), sizeof(wchar_t), buffer.size(), f);
+ internal::fwrite_fully(buffer.data(), sizeof(wchar_t), buffer.size(), f);
}
FMT_FUNC void vprint(string_view format_str, format_args args) {