#ifndef FMT_CORE_H_
#define FMT_CORE_H_
-#include <cassert>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 60000
+#define FMT_VERSION 60102
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
# 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
#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
# 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
# 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))) || \
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 {};
// 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)
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");
: 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,
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;
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>
};
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);
/** 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_;
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.
+ /**
+ 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(int) {
- 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>) {}
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;
: 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_;
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_; }
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,
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_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_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 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);
};
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;
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) {}
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));
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)>
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; }
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 && !has_formatter<T, Context>::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);
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 &&
- (has_formatter<T, Context>::value ||
- has_fallback_formatter<T, Context>::value))>
+ 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 &&
+ !std::is_constructible<std_string_view<char_type>, T>::value)))>
FMT_CONSTEXPR const T& map(const T& val) {
return val;
}
// 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
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);
}
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_); }
};
/**
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:
// 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 {
}
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_; }
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;
};
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>
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.
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)...} {}
bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
internal::type type(int index) const {
- int shift = index * 4;
- return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
+ 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>;
*/
template <typename... Args>
basic_format_args(const format_arg_store<Context, Args...>& store)
- : types_(static_cast<unsigned long long>(store.types)) {
+ : types_(store.types) {
set_data(store.data_);
}
}
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
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``.
-// Formatting library for C++
+// Formatting library for C++ - implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
#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)
#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
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.
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;
- auto abs_value = static_cast<uint32_or_64_t<int>>(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;
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();
} // 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;
}
}
#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) {
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;
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
}
-template <typename T>
-int format_float(char* buf, std::size_t size, const char* format, int precision,
- T value) {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- if (precision > 100000)
- throw std::runtime_error(
- "fuzz mode - avoid large allocation inside snprintf");
-#endif
- // Suppress the warning about nonliteral format string.
- auto snprintf_ptr = FMT_SNPRINTF;
- return precision < 0 ? snprintf_ptr(buf, size, format, value)
- : snprintf_ptr(buf, size, format, precision, value);
-}
-
template <typename T>
const char basic_data<T>::digits[] =
"0001020304050607080910111213141516171819"
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};
+ 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,
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};
+ 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.
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:
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;
// 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].
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;
+ 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;
- 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
// 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.
// 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;
};
// 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).
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);
}
};
-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;
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>
-char* sprintf_format(Double value, internal::buffer<char>& buf,
- sprintf_specs specs) {
+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 (specs.alt || !specs.type) *format_ptr++ = '#';
- if (specs.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 = specs.type;
-
- if (type == '%')
- type = 'f';
- else if (type == 0 || type == 'n')
- 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;
- char* decimal_point_pos = nullptr;
+ auto offset = buf.size();
for (;;) {
- std::size_t buffer_size = buf.capacity();
- start = &buf[0];
- int result =
- format_float(start, buffer_size, format, specs.precision, value);
- if (result >= 0) {
- 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 (specs.type != 'a' && specs.type != 'A') {
- while (p < end && *p >= '0' && *p <= '9') ++p;
- if (p < end && *p != 'e' && *p != 'E') {
- decimal_point_pos = p;
- if (!specs.type) {
- // 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;
}
- return decimal_point_pos;
}
} // 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) {
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);
- buffer.push_back(L'\0');
- if (std::fputws(buffer.data(), f) == -1) {
- FMT_THROW(system_error(errno, "cannot write to file"));
- }
-}
-
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
#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
# define FMT_HAS_BUILTIN(x) 0
#endif
+#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) && \
+ (__cplusplus >= 201703 || FMT_GCC_VERSION != 0)
+# define FMT_FALLTHROUGH [[fallthrough]]
+#else
+# define FMT_FALLTHROUGH
+#endif
+
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# if FMT_MSC_VER
}
} // 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
# define FMT_THROW(x) \
do { \
static_cast<void>(sizeof(x)); \
- assert(false); \
+ FMT_ASSERT(false, ""); \
} while (false)
# endif
#endif
# 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
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)
_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 approximation of iterator_t for pre-C++20 systems.
// An output iterator that counts the number of objects written to it and
// discards them.
-template <typename T> class counting_iterator {
+class counting_iterator {
private:
std::size_t count_;
- mutable T blackhole_;
public:
using iterator_category = std::output_iterator_tag;
- using value_type = T;
using difference_type = std::ptrdiff_t;
- using pointer = T*;
- using reference = 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_; }
return it;
}
- T& operator*() const { return blackhole_; }
+ value_type operator*() const { return {}; }
};
template <typename OutputIt> class truncating_iterator_base {
sentinel end() const { return {}; } // Sentinel is not used yet.
};
-// A range with an iterator appending to a buffer.
-template <typename T>
-class buffer_range
- : public output_range<std::back_insert_iterator<buffer<T>>, T> {
- public:
- using iterator = std::back_insert_iterator<buffer<T>>;
- using output_range<iterator, T>::output_range;
- buffer_range(buffer<T>& buf)
- : output_range<iterator, T>(std::back_inserter(buf)) {}
-};
-
template <typename Char>
inline size_t count_code_points(basic_string_view<Char> s) {
return s.size();
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>
}
#ifndef FMT_USE_GRISU
-# define FMT_USE_GRISU 0
+# define FMT_USE_GRISU 1
#endif
template <typename T> constexpr bool use_grisu() {
}
} // 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:
: Allocator(alloc) {
this->set(store_, SIZE);
}
- ~basic_memory_buffer() { deallocate(); }
+ ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
private:
// Move data from other to this buffer.
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;
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;
};
namespace internal {
return false;
}
-// Smallest of uint32_t and uint64_t that is large enough to represent all
-// values of 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_t =
- conditional_t<std::numeric_limits<T>::digits <= 32, uint32_t, uint64_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 for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
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>;
}
#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;
template <> int count_digits<4>(internal::fallback_uintptr n);
-#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) {
- 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)
- : h.template on<3>(n)
- : n < 100000 ? h.template on<4>(n)
- : h.template on<5>(n)
- : n < 100000000 ? n < 10000000 ? h.template on<6>(n)
- : h.template on<7>(n)
- : n < 1000000000 ? h.template on<8>(n)
- : h.template on<9>(n);
-}
-
-// An lg handler that formats a decimal number.
-// Usage: lg(n, decimal_formatter(buffer));
-class decimal_formatter {
- private:
- char* buffer_;
-
- void write_pair(unsigned N, uint32_t index) {
- std::memcpy(buffer_ + N, data::digits + index * 2, 2);
- }
-
- public:
- explicit decimal_formatter(char* buf) : buffer_(buf) {}
-
- template <unsigned N> char* on(uint32_t u) {
- if (N == 0) {
- *buffer_ = static_cast<char>(u) + '0';
- } else if (N == 1) {
- write_pair(0, u);
- } else {
- // The idea of using 4.32 fixed-point numbers is based on
- // https://github.com/jeaiii/itoa
- 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);
- t = ((t * u) >> a) + n / 5 * 4;
- write_pair(0, t >> 32);
- for (unsigned i = 2; i < N; i += 2) {
- t = 100ULL * static_cast<uint32_t>(t);
- write_pair(i, t >> 32);
- }
- if (N % 2 == 0) {
- buffer_[N] =
- static_cast<char>((10ULL * static_cast<uint32_t>(t)) >> 32) + '0';
- }
- }
- return buffer_ += N + 1;
- }
-};
-
#ifdef FMT_BUILTIN_CLZ
// Optional version of count_digits for better performance on 32-bit platforms.
inline int count_digits(uint32_t n) {
}
#endif
+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));
// 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]);
add_thousands_sep(buffer);
*--buffer = static_cast<Char>('0' + value);
return end;
}
- unsigned index = static_cast<unsigned>(value * 2);
+ 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 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,
F add_thousands_sep) {
FMT_ASSERT(num_digits >= 0, "invalid digit count");
// Buffer should be large enough to hold all digits (<= digits10 + 1).
- enum { max_size = std::numeric_limits<UInt>::digits10 + 1 };
- Char buffer[max_size + max_size / 3];
+ 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 <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).
- char buffer[std::numeric_limits<UInt>::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);
}
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 {
+// 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 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;
*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;
return it;
}
-struct gen_digits_params {
- int num_digits;
- bool fixed;
- bool upper;
- bool trailing_zeros;
-};
-
-// 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, Char decimal_point) {
- // 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++ = decimal_point;
- 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++ = decimal_point;
+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);
+ }
+ 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");
+ 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 (!params.trailing_zeros) {
- // Remove trailing zeros.
- while (size > full_exp && digits[size - 1] == '0') --size;
- if (size != full_exp) *it++ = decimal_point;
- return copy_str<Char>(digits + full_exp, digits + size, it);
- }
- *it++ = decimal_point;
- 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'));
- }
- } else {
- // 1234e-6 -> 0.001234
- *it++ = static_cast<Char>('0');
- int num_zeros = -full_exp;
- if (params.num_digits >= 0 && params.num_digits < num_zeros)
- num_zeros = params.num_digits;
- if (!params.trailing_zeros)
- while (size > 0 && digits[size - 1] == '0') --size;
- if (num_zeros != 0 || size != 0) {
- *it++ = decimal_point;
- it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
- it = copy_str<Char>(digits, digits + size, it);
+ 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);
+ }
}
+ return it;
}
- return it;
-}
-namespace grisu_options {
-enum { fixed = 1, grisu3 = 2 };
-}
+ 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;
+ }
+ size_ = prettify(counting_iterator()).count();
+ size_ += specs.sign ? 1 : 0;
+ }
-// 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;
-}
+ size_t size() const { return size_; }
+ size_t width() const { return size(); }
-struct sprintf_specs {
- int precision;
- char type;
- bool alt : 1;
+ template <typename It> void operator()(It&& it) {
+ if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
+ it = prettify(it);
+ }
+};
- template <typename Char>
- constexpr sprintf_specs(basic_format_specs<Char> specs)
- : precision(specs.precision), type(specs.type), alt(specs.alt) {}
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
- constexpr bool has_precision() const { return precision >= 0; }
-};
+// 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 Double>
-char* sprintf_format(Double, internal::buffer<char>&, sprintf_specs);
+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) {
}
}
-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':
- handler.on_num();
+ result.locale = true;
break;
default:
- handler.on_error();
+ eh.on_error("invalid type specifier");
break;
}
+ return result;
}
template <typename Char, typename Handler>
}
};
-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_num() {}
-
- FMT_CONSTEXPR void on_error() {
- ErrorHandler::on_error("invalid type specifier");
- }
-};
-
template <typename ErrorHandler>
class char_specs_checker : public ErrorHandler {
private:
}
}
+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 {
private:
iterator out_; // Output iterator.
- internal::locale_ref locale_;
+ 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_.
template <typename It> void operator()(It&& it) const {
if (prefix.size() != 0)
- it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
+ it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = std::fill_n(it, padding, fill);
f(it);
}
// 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() + internal::to_unsigned(num_digits);
+ 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 = internal::to_unsigned(specs.width);
+ 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() + internal::to_unsigned(specs.precision);
- padding = internal::to_unsigned(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;
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
- auto abs_value = static_cast<uint32_or_64_t<Int>>(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);
+ 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_t<Int>;
+ using unsigned_type = uint32_or_64_or_128_t<Int>;
basic_writer<Range>& writer;
const Specs& specs;
specs(s),
abs_value(static_cast<unsigned_type>(value)),
prefix_size(0) {
- if (internal::is_negative(value)) {
+ if (is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
};
void on_dec() {
- int num_digits = internal::count_digits(abs_value);
+ int num_digits = count_digits(abs_value);
writer.write_int(num_digits, get_prefix(), specs,
dec_writer{abs_value, num_digits});
}
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.specs.type != 'x');
+ it = format_uint<4, char_type>(it, self.abs_value, num_digits,
+ self.specs.type != 'x');
}
};
prefix[prefix_size++] = '0';
prefix[prefix_size++] = specs.type;
}
- int num_digits = internal::count_digits<4>(abs_value);
+ int num_digits = count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), specs,
hex_writer{*this, num_digits});
}
int num_digits;
template <typename It> void operator()(It&& it) const {
- it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
+ it = format_uint<BITS, char_type>(it, abs_value, num_digits);
}
};
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(specs.type);
}
- int num_digits = internal::count_digits<1>(abs_value);
+ 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 = internal::count_digits<3>(abs_value);
- if (specs.alt && specs.precision <= num_digits) {
+ 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';
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.
- unsigned digit_index = 0;
- it = internal::format_decimal<char_type>(
- it, abs_value, size, [s, &digit_index](char_type*& buffer) {
- if (++digit_index % 3 != 0) return;
+ 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(),
- internal::make_checked(buffer, s.size()));
+ make_checked(buffer, s.size()));
});
}
};
void on_num() {
- char_type sep = internal::thousands_sep<char_type>(writer.locale_);
+ 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 = internal::count_digits(abs_value);
- int size = num_digits + sep_size * ((num_digits - 1) / 3);
+ 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, sep});
+ num_writer{abs_value, size, groups, sep});
}
FMT_NORETURN void on_error() {
}
};
- 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;
- char* decimal_point_pos;
- char_type decimal_point;
-
- 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);
- auto begin = buffer.begin();
- if (decimal_point_pos) {
- it = internal::copy_str<char_type>(begin, decimal_point_pos, it);
- *it++ = decimal_point;
- begin = decimal_point_pos + 1;
- }
- it = internal::copy_str<char_type>(begin, buffer.end(), it);
- }
- };
-
- class grisu_writer {
- private:
- internal::buffer<char>& digits_;
- size_t size_;
- char sign_;
- int exp_;
- internal::gen_digits_params params_;
- char_type decimal_point_;
-
- public:
- grisu_writer(char sign, internal::buffer<char>& digits, int exp,
- const internal::gen_digits_params& params,
- char_type decimal_point)
- : digits_(digits),
- sign_(sign),
- exp_(exp),
- params_(params),
- decimal_point_(decimal_point) {
- 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_, decimal_point_);
- }
- };
-
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_));
+ return 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);
+ it = copy_str<char_type>(s, s + size_, it);
}
};
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);
+ it = format_uint<4, char_type>(it, value, num_digits);
}
};
public:
- /** Constructs a ``basic_writer`` object. */
- explicit basic_writer(Range out,
- internal::locale_ref loc = internal::locale_ref())
+ explicit basic_writer(Range out, locale_ref loc = locale_ref())
: out_(out.begin()), locale_(loc) {}
iterator out() const { return out_; }
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
- // Writes a formatted integer.
+#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) {
- internal::handle_int_type_spec(spec.type,
- int_writer<T, Spec>(*this, value, spec));
+ handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
}
- void write(double value, const format_specs& specs = format_specs()) {
- write_double(value, specs);
- }
+ 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;
+ }
- /**
- \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& specs = format_specs()) {
- write_double(value, specs);
- }
+ 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});
+ }
- // Formats a floating-point number (double or long double).
- template <typename T, bool USE_GRISU = fmt::internal::use_grisu<T>()>
- void write_double(T value, const format_specs& specs);
+ 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));
+ }
- /** Writes a character to the buffer. */
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
*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);
+ 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, "");
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 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 = format_specs()) {
+ 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 && internal::to_unsigned(specs.precision) < size)
- size = internal::to_unsigned(specs.precision);
+ 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 = internal::count_digits<4>(value);
+ 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;
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:
}
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:
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_);
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();
}
template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
ErrorHandler&& eh) {
- assert(begin != end && '0' <= *begin && *begin <= '9');
+ FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
if (*begin == '0') {
++begin;
return 0;
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
- constexpr unsigned max_int = (std::numeric_limits<int>::max)();
+ constexpr unsigned max_int = max_value<int>();
unsigned big = max_int / 10;
do {
// Check for overflow.
private:
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) {}
template <typename T>
using is_integer =
- bool_constant<std::is_integral<T>::value && !std::is_same<T, bool>::value &&
+ 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>;
: 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");
}
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 {};
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); }
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_(to_unsigned(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.
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(string_view_metadata name)
+ FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
: kind(arg_id_kind::name), val(name) {}
FMT_CONSTEXPR arg_ref& operator=(int idx) {
arg_id_kind kind;
union value {
- FMT_CONSTEXPR value() : index(0u) {}
- FMT_CONSTEXPR value(int 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) {}
int index;
- string_view_metadata name;
+ basic_string_view<Char> name;
} val;
};
context_.check_arg_id(arg_id);
basic_string_view<char_type> format_str(
context_.begin(), to_unsigned(context_.end() - context_.begin()));
- const auto id_metadata = string_view_metadata(format_str, arg_id);
- return arg_ref_type(id_metadata);
+ return arg_ref_type(arg_id);
}
dynamic_format_specs<char_type>& specs_;
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') {
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;
case '>':
align = align::right;
break;
+#if FMT_NUMERIC_ALIGN
case '=':
align = align::numeric;
break;
+#endif
case '^':
align = align::center;
break;
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 (;;) {
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);
}
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
- : arg_id_((std::numeric_limits<unsigned>::max)()),
+ : arg_id_(-1),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
}
private:
- using parse_context_type = basic_parse_context<Char, ErrorHandler>;
+ using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
enum { num_args = sizeof...(Args) };
FMT_CONSTEXPR void check_arg_id() {
// Format specifier parsing function.
using parse_func = const Char* (*)(parse_context_type&);
- unsigned arg_id_;
+ int arg_id_;
parse_context_type context_;
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
};
(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) {
+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_id_kind::none:
break;
case arg_id_kind::index:
- internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
- ctx.error_handler());
+ value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+ ctx.error_handler());
break;
- case arg_id_kind::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());
+ case arg_id_kind::name:
+ value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+ ctx.error_handler());
break;
}
- }
}
} // namespace internal
template <typename Range>
-using basic_writer FMT_DEPRECATED = internal::basic_writer<Range>;
-using writer FMT_DEPRECATED = internal::writer;
-using wwriter FMT_DEPRECATED =
- internal::basic_writer<internal::buffer_range<wchar_t>>;
+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>
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:
using range = Range;
*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* specs = nullptr)
+ 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) {}
/** 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();
}
};
: 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_; }
};
\endrst
*/
FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
- fmt::string_view message) FMT_NOEXCEPT;
-
-struct float_spec_handler {
- char type;
- bool upper;
- bool fixed;
- bool as_percentage;
- bool use_locale;
-
- explicit float_spec_handler(char t)
- : type(t),
- upper(false),
- fixed(false),
- as_percentage(false),
- use_locale(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;
- }
-
- void on_num() { use_locale = true; }
-
- FMT_NORETURN void on_error() {
- FMT_THROW(format_error("invalid type specifier"));
- }
-};
-
-template <typename Range>
-template <typename T, bool USE_GRISU>
-void internal::basic_writer<Range>::write_double(T value,
- const format_specs& specs) {
- // Check type.
- float_spec_handler handler(static_cast<char>(specs.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 (specs.sign != sign::none) {
- if (specs.sign == sign::plus)
- sign = '+';
- else if (specs.sign == sign::space)
- sign = ' ';
- }
-
- 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(specs,
- inf_or_nan_writer{sign, handler.as_percentage, str});
- }
-
- if (handler.as_percentage) value *= 100;
-
- memory_buffer buffer;
- int exp = 0;
- int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
- unsigned options = handler.fixed ? internal::grisu_options::fixed : 0;
- bool use_grisu = USE_GRISU &&
- (specs.type != 'a' && specs.type != 'A' &&
- specs.type != 'e' && specs.type != 'E') &&
- internal::grisu_format(static_cast<double>(value), buffer,
- precision, options, exp);
- char* decimal_point_pos = nullptr;
- if (!use_grisu)
- decimal_point_pos = internal::sprintf_format(value, buffer, specs);
-
- if (handler.as_percentage) {
- buffer.push_back('%');
- --exp; // Adjust decimal place position.
- }
- format_specs as = specs;
- if (specs.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 (specs.align == align::none) {
- as.align = align::right;
- }
- char_type decimal_point = handler.use_locale
- ? internal::decimal_point<char_type>(locale_)
- : static_cast<char_type>('.');
- if (use_grisu) {
- auto params = internal::gen_digits_params();
- params.fixed = handler.fixed;
- params.num_digits = precision;
- params.trailing_zeros =
- (precision != 0 && (handler.fixed || !specs.type)) || specs.alt;
- write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point));
- } else {
- write_padded(as,
- double_writer{sign, buffer, decimal_point_pos, decimal_point});
- }
-}
+ string_view message) FMT_NOEXCEPT;
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
// 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 = static_cast<char>('0' + value);
return ptr;
}
- unsigned index = static_cast<unsigned>(value * 2);
+ 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);
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),
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));
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(
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>;
private:
internal::dynamic_format_specs<Char> specs_;
- const Char* format_str_;
};
#define FMT_FORMAT_AS(Type, Base) \
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*);
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_;
}
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()));
}
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) {
if (visit_format_arg(f, arg)) return parse_context.begin();
basic_format_specs<Char> specs;
using internal::specs_handler;
- using parse_context_t = basic_parse_context<Char>;
+ 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());
return begin;
}
- basic_parse_context<Char> parse_context;
+ basic_format_parse_context<Char> parse_context;
Context context;
basic_format_arg<Context> arg;
};
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_context =
- format_context_t<fmt::internal::truncating_iterator<OutputIt>, Char>;
+ format_context_t<internal::truncating_iterator<OutputIt>, Char>;
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
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);
}
/**
*/
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
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, std::forward<Args>(args)...);
#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.
std::string s = format(FMT_STRING("{:d}"), "foo");
\endrst
*/
-#define FMT_STRING(s) \
- [] { \
- struct str : fmt::compile_string { \
- using char_type = typename std::remove_cv<std::remove_pointer< \
- typename std::decay<decltype(s)>::type>::type>::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