mirror of
https://github.com/gabime/spdlog.git
synced 2025-01-24 06:32:06 +08:00
Bump fmt to version 8.1.1 and run clang-format
This commit is contained in:
parent
b7836c33ae
commit
d8199b607d
@ -143,6 +143,8 @@ class dynamic_format_arg_store
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
constexpr dynamic_format_arg_store() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Adds an argument into the dynamic store for later passing to a formatting
|
Adds an argument into the dynamic store for later passing to a formatting
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -185,9 +185,13 @@ enum class terminal_color : uint8_t {
|
|||||||
|
|
||||||
enum class emphasis : uint8_t {
|
enum class emphasis : uint8_t {
|
||||||
bold = 1,
|
bold = 1,
|
||||||
italic = 1 << 1,
|
faint = 1 << 1,
|
||||||
underline = 1 << 2,
|
italic = 1 << 2,
|
||||||
strikethrough = 1 << 3
|
underline = 1 << 3,
|
||||||
|
blink = 1 << 4,
|
||||||
|
reverse = 1 << 5,
|
||||||
|
conceal = 1 << 6,
|
||||||
|
strikethrough = 1 << 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
// rgb is a struct for red, green and blue colors.
|
// rgb is a struct for red, green and blue colors.
|
||||||
@ -409,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
buffer[19] = static_cast<Char>(0);
|
buffer[19] = static_cast<Char>(0);
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
uint8_t em_codes[4] = {};
|
uint8_t em_codes[num_emphases] = {};
|
||||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||||
em_codes[3] = 9;
|
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||||
|
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||||
|
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (size_t i = 0; i < num_emphases; ++i) {
|
||||||
if (!em_codes[i]) continue;
|
if (!em_codes[i]) continue;
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
buffer[index++] = static_cast<Char>('[');
|
buffer[index++] = static_cast<Char>('[');
|
||||||
@ -435,7 +441,8 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Char buffer[7u + 3u * 4u + 1u];
|
static constexpr size_t num_emphases = 8;
|
||||||
|
Char buffer[7u + 3u * num_emphases + 1u];
|
||||||
|
|
||||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||||
char delimiter) FMT_NOEXCEPT {
|
char delimiter) FMT_NOEXCEPT {
|
||||||
@ -444,6 +451,10 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
out[2] = static_cast<Char>('0' + c % 10);
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
out[3] = static_cast<Char>(delimiter);
|
out[3] = static_cast<Char>(delimiter);
|
||||||
}
|
}
|
||||||
|
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
|
||||||
|
emphasis mask) FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -156,7 +156,7 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
|||||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
#ifdef __cpp_if_constexpr
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
# define FMT_COMPILE(s) \
|
# define FMT_COMPILE(s) \
|
||||||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||||
#else
|
#else
|
||||||
@ -179,7 +179,7 @@ const T& first(const T& value, const Tail&...) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
template <typename... Args> struct type_list {};
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
@ -190,7 +190,7 @@ constexpr const auto& get([[maybe_unused]] const T& first,
|
|||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
else
|
else
|
||||||
return get<N - 1>(rest...);
|
return detail::get<N - 1>(rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename... Args>
|
template <typename Char, typename... Args>
|
||||||
@ -202,7 +202,8 @@ constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
|||||||
template <int N, typename> struct get_type_impl;
|
template <int N, typename> struct get_type_impl;
|
||||||
|
|
||||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
using type =
|
||||||
|
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int N, typename T>
|
template <int N, typename T>
|
||||||
@ -242,7 +243,7 @@ template <typename Char> struct code_unit {
|
|||||||
// This ensures that the argument type is convertible to `const T&`.
|
// This ensures that the argument type is convertible to `const T&`.
|
||||||
template <typename T, int N, typename... Args>
|
template <typename T, int N, typename... Args>
|
||||||
constexpr const T& get_arg_checked(const Args&... args) {
|
constexpr const T& get_arg_checked(const Args&... args) {
|
||||||
const auto& arg = get<N>(args...);
|
const auto& arg = detail::get<N>(args...);
|
||||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||||
return arg.value;
|
return arg.value;
|
||||||
} else {
|
} else {
|
||||||
@ -289,7 +290,7 @@ template <typename Char> struct runtime_named_field {
|
|||||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
bool found = (try_format_argument(out, name, args) || ...);
|
bool found = (try_format_argument(out, name, args) || ...);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw format_error("argument with specified name is not found");
|
FMT_THROW(format_error("argument with specified name is not found"));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@ -399,7 +400,9 @@ template <typename Char> struct arg_id_handler {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void on_error(const char* message) { throw format_error(message); }
|
constexpr void on_error(const char* message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char> struct parse_arg_id_result {
|
template <typename Char> struct parse_arg_id_result {
|
||||||
@ -451,7 +454,7 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
if constexpr (str[POS] == '{') {
|
if constexpr (str[POS] == '{') {
|
||||||
if constexpr (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '{' in format string");
|
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||||
if constexpr (str[POS + 1] == '{') {
|
if constexpr (str[POS + 1] == '{') {
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||||
@ -500,7 +503,7 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
}
|
}
|
||||||
} else if constexpr (str[POS] == '}') {
|
} else if constexpr (str[POS] == '}') {
|
||||||
if constexpr (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '}' in format string");
|
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
@ -527,12 +530,12 @@ constexpr auto compile(S format_str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // __cpp_if_constexpr
|
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_MODULE_EXPORT_BEGIN
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -40,6 +40,10 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
|||||||
std::terminate();
|
std::terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void throw_format_error(const char* message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
# define FMT_SNPRINTF snprintf
|
# define FMT_SNPRINTF snprintf
|
||||||
#else // _MSC_VER
|
#else // _MSC_VER
|
||||||
@ -145,141 +149,13 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
|
|||||||
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
|
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __cplusplus < 201703L
|
// log10(2) = 0x0.4d104d427de7fbcc...
|
||||||
template <typename T> constexpr const char basic_data<T>::digits[][2];
|
static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc;
|
||||||
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
|
||||||
template <typename T> constexpr const char basic_data<T>::signs[];
|
|
||||||
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
|
|
||||||
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
|
||||||
template <typename T>
|
|
||||||
constexpr const char basic_data<T>::right_padding_shifts[];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename T> struct bits {
|
template <typename T = void> struct basic_impl_data {
|
||||||
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:
|
|
||||||
using significand_type = uint64_t;
|
|
||||||
|
|
||||||
template <typename Float>
|
|
||||||
using is_supported_float = bool_constant<sizeof(Float) == sizeof(uint64_t) ||
|
|
||||||
sizeof(Float) == sizeof(uint32_t)>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
significand_type f;
|
|
||||||
int e;
|
|
||||||
|
|
||||||
// All sizes are in bits.
|
|
||||||
// Subtract 1 to account for an implicit most significant bit in the
|
|
||||||
// normalized form.
|
|
||||||
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;
|
|
||||||
static FMT_CONSTEXPR_DECL const int significand_size =
|
|
||||||
bits<significand_type>::value;
|
|
||||||
|
|
||||||
fp() : f(0), e(0) {}
|
|
||||||
fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
|
||||||
|
|
||||||
// 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) { assign(d); }
|
|
||||||
|
|
||||||
// Assigns d to this and return true iff predecessor is closer than successor.
|
|
||||||
template <typename Float, FMT_ENABLE_IF(is_supported_float<Float>::value)>
|
|
||||||
bool assign(Float d) {
|
|
||||||
// Assume float is in the format [sign][exponent][significand].
|
|
||||||
using limits = std::numeric_limits<Float>;
|
|
||||||
const int float_significand_size = limits::digits - 1;
|
|
||||||
const int exponent_size =
|
|
||||||
bits<Float>::value - float_significand_size - 1; // -1 for sign
|
|
||||||
const uint64_t float_implicit_bit = 1ULL << float_significand_size;
|
|
||||||
const uint64_t significand_mask = float_implicit_bit - 1;
|
|
||||||
const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
|
|
||||||
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
|
||||||
constexpr bool is_double = sizeof(Float) == sizeof(uint64_t);
|
|
||||||
auto u = bit_cast<conditional_t<is_double, uint64_t, uint32_t>>(d);
|
|
||||||
f = u & significand_mask;
|
|
||||||
int biased_e =
|
|
||||||
static_cast<int>((u & exponent_mask) >> float_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 += float_implicit_bit;
|
|
||||||
else
|
|
||||||
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
|
||||||
e = biased_e - exponent_bias - float_significand_size;
|
|
||||||
return is_predecessor_closer;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Float, FMT_ENABLE_IF(!is_supported_float<Float>::value)>
|
|
||||||
bool assign(Float) {
|
|
||||||
*this = fp();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
|
||||||
template <int SHIFT> fp normalize(fp value) {
|
|
||||||
// Handle subnormals.
|
|
||||||
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
|
|
||||||
while ((value.f & shifted_implicit_bit) == 0) {
|
|
||||||
value.f <<= 1;
|
|
||||||
--value.e;
|
|
||||||
}
|
|
||||||
// Subtract 1 to account for hidden bit.
|
|
||||||
const auto offset =
|
|
||||||
fp::significand_size - fp::double_significand_size - SHIFT - 1;
|
|
||||||
value.f <<= offset;
|
|
||||||
value.e -= offset;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
|
||||||
|
|
||||||
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
|
||||||
inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
|
||||||
#if FMT_USE_INT128
|
|
||||||
auto product = static_cast<__uint128_t>(lhs) * rhs;
|
|
||||||
auto f = static_cast<uint64_t>(product >> 64);
|
|
||||||
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 = 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 ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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`.
|
|
||||||
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
|
||||||
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||||
// These are generated by support/compute-powers.py.
|
// These are generated by support/compute-powers.py.
|
||||||
static constexpr const uint64_t pow10_significands[] = {
|
static constexpr uint64_t pow10_significands[87] = {
|
||||||
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||||
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||||
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||||
@ -311,9 +187,13 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
|||||||
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wnarrowing"
|
||||||
|
#endif
|
||||||
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
// to significands above.
|
// to significands above.
|
||||||
static constexpr const int16_t pow10_exponents[] = {
|
static constexpr int16_t pow10_exponents[87] = {
|
||||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||||
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||||
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||||
@ -322,11 +202,137 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
|||||||
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||||
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||||
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static constexpr uint64_t power_of_10_64[20] = {
|
||||||
|
1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
|
||||||
|
10000000000000000000ULL};
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
|
||||||
|
struct impl_data : basic_impl_data<> {};
|
||||||
|
|
||||||
|
#if __cplusplus < 201703L
|
||||||
|
template <typename T>
|
||||||
|
constexpr uint64_t basic_impl_data<T>::pow10_significands[];
|
||||||
|
template <typename T> constexpr int16_t basic_impl_data<T>::pow10_exponents[];
|
||||||
|
template <typename T> constexpr uint64_t basic_impl_data<T>::power_of_10_64[];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T> struct bits {
|
||||||
|
static FMT_CONSTEXPR_DECL const int value =
|
||||||
|
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the number of significand bits in Float excluding the implicit bit.
|
||||||
|
template <typename Float> constexpr int num_significand_bits() {
|
||||||
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
|
// normalized form.
|
||||||
|
return std::numeric_limits<Float>::digits - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A floating-point number f * pow(2, e).
|
||||||
|
struct fp {
|
||||||
|
uint64_t f;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
static constexpr const int num_significand_bits = bits<decltype(f)>::value;
|
||||||
|
|
||||||
|
constexpr fp() : f(0), e(0) {}
|
||||||
|
constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
||||||
|
|
||||||
|
// Constructs fp from an IEEE754 floating-point number. It is a template to
|
||||||
|
// prevent compile errors on systems where n is not IEEE754.
|
||||||
|
template <typename Float> explicit FMT_CONSTEXPR fp(Float n) { assign(n); }
|
||||||
|
|
||||||
|
template <typename Float>
|
||||||
|
using is_supported = bool_constant<sizeof(Float) == sizeof(uint64_t) ||
|
||||||
|
sizeof(Float) == sizeof(uint32_t)>;
|
||||||
|
|
||||||
|
// Assigns d to this and return true iff predecessor is closer than successor.
|
||||||
|
template <typename Float, FMT_ENABLE_IF(is_supported<Float>::value)>
|
||||||
|
FMT_CONSTEXPR bool assign(Float n) {
|
||||||
|
// Assume float is in the format [sign][exponent][significand].
|
||||||
|
const int num_float_significand_bits =
|
||||||
|
detail::num_significand_bits<Float>();
|
||||||
|
const uint64_t implicit_bit = 1ULL << num_float_significand_bits;
|
||||||
|
const uint64_t significand_mask = implicit_bit - 1;
|
||||||
|
constexpr bool is_double = sizeof(Float) == sizeof(uint64_t);
|
||||||
|
auto u = bit_cast<conditional_t<is_double, uint64_t, uint32_t>>(n);
|
||||||
|
f = u & significand_mask;
|
||||||
|
const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
|
||||||
|
int biased_e =
|
||||||
|
static_cast<int>((u & exponent_mask) >> num_float_significand_bits);
|
||||||
|
// The predecessor is closer if n 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).
|
||||||
|
const int exponent_bias = std::numeric_limits<Float>::max_exponent - 1;
|
||||||
|
e = biased_e - exponent_bias - num_float_significand_bits;
|
||||||
|
return is_predecessor_closer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Float, FMT_ENABLE_IF(!is_supported<Float>::value)>
|
||||||
|
bool assign(Float) {
|
||||||
|
FMT_ASSERT(false, "");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT = 0> FMT_CONSTEXPR fp normalize(fp value) {
|
||||||
|
// Handle subnormals.
|
||||||
|
const uint64_t implicit_bit = 1ULL << num_significand_bits<double>();
|
||||||
|
const auto shifted_implicit_bit = 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::num_significand_bits - num_significand_bits<double>() - SHIFT - 1;
|
||||||
|
value.f <<= offset;
|
||||||
|
value.e -= offset;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
||||||
|
|
||||||
|
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
||||||
|
FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
||||||
|
#if FMT_USE_INT128
|
||||||
|
auto product = static_cast<__uint128_t>(lhs) * rhs;
|
||||||
|
auto f = static_cast<uint64_t>(product >> 64);
|
||||||
|
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 = 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 ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR 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_CONSTEXPR inline fp get_cached_power(int min_exponent,
|
||||||
|
int& pow10_exponent) {
|
||||||
const int shift = 32;
|
const int shift = 32;
|
||||||
const auto significand = static_cast<int64_t>(data::log10_2_significand);
|
const auto significand = static_cast<int64_t>(log10_2_significand);
|
||||||
int index = static_cast<int>(
|
int index = static_cast<int>(
|
||||||
((min_exponent + fp::significand_size - 1) * (significand >> shift) +
|
((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
|
||||||
((int64_t(1) << shift) - 1)) // ceil
|
((int64_t(1) << shift) - 1)) // ceil
|
||||||
>> 32 // arithmetic shift
|
>> 32 // arithmetic shift
|
||||||
);
|
);
|
||||||
@ -336,7 +342,8 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
|||||||
const int dec_exp_step = 8;
|
const int dec_exp_step = 8;
|
||||||
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
return {pow10_significands[index], pow10_exponents[index]};
|
return {impl_data::pow10_significands[index],
|
||||||
|
impl_data::pow10_exponents[index]};
|
||||||
}
|
}
|
||||||
|
|
||||||
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
|
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
|
||||||
@ -345,14 +352,16 @@ struct accumulator {
|
|||||||
uint64_t lower;
|
uint64_t lower;
|
||||||
uint64_t upper;
|
uint64_t upper;
|
||||||
|
|
||||||
accumulator() : lower(0), upper(0) {}
|
constexpr accumulator() : lower(0), upper(0) {}
|
||||||
explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
|
constexpr explicit operator uint32_t() const {
|
||||||
|
return static_cast<uint32_t>(lower);
|
||||||
|
}
|
||||||
|
|
||||||
void operator+=(uint64_t n) {
|
FMT_CONSTEXPR void operator+=(uint64_t n) {
|
||||||
lower += n;
|
lower += n;
|
||||||
if (lower < n) ++upper;
|
if (lower < n) ++upper;
|
||||||
}
|
}
|
||||||
void operator>>=(int shift) {
|
FMT_CONSTEXPR void operator>>=(int shift) {
|
||||||
FMT_ASSERT(shift == 32, "");
|
FMT_ASSERT(shift == 32, "");
|
||||||
(void)shift;
|
(void)shift;
|
||||||
lower = (upper << 32) | (lower >> 32);
|
lower = (upper << 32) | (lower >> 32);
|
||||||
@ -370,27 +379,31 @@ class bigint {
|
|||||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||||
int exp_;
|
int exp_;
|
||||||
|
|
||||||
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
|
FMT_CONSTEXPR20 bigit operator[](int index) const {
|
||||||
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
|
return bigits_[to_unsigned(index)];
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR20 bigit& operator[](int index) {
|
||||||
|
return bigits_[to_unsigned(index)];
|
||||||
|
}
|
||||||
|
|
||||||
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
||||||
|
|
||||||
friend struct formatter<bigint>;
|
friend struct formatter<bigint>;
|
||||||
|
|
||||||
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||||
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||||
(*this)[index] = static_cast<bigit>(result);
|
(*this)[index] = static_cast<bigit>(result);
|
||||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_leading_zeros() {
|
FMT_CONSTEXPR20 void remove_leading_zeros() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||||
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||||
bigits_.resize(to_unsigned(num_bigits + 1));
|
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||||
void subtract_aligned(const bigint& other) {
|
FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
|
||||||
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
||||||
FMT_ASSERT(compare(*this, other) >= 0, "");
|
FMT_ASSERT(compare(*this, other) >= 0, "");
|
||||||
bigit borrow = 0;
|
bigit borrow = 0;
|
||||||
@ -401,7 +414,7 @@ class bigint {
|
|||||||
remove_leading_zeros();
|
remove_leading_zeros();
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(uint32_t value) {
|
FMT_CONSTEXPR20 void multiply(uint32_t value) {
|
||||||
const double_bigit wide_value = value;
|
const double_bigit wide_value = value;
|
||||||
bigit carry = 0;
|
bigit carry = 0;
|
||||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||||
@ -412,7 +425,7 @@ class bigint {
|
|||||||
if (carry != 0) bigits_.push_back(carry);
|
if (carry != 0) bigits_.push_back(carry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(uint64_t value) {
|
FMT_CONSTEXPR20 void multiply(uint64_t value) {
|
||||||
const bigit mask = ~bigit(0);
|
const bigit mask = ~bigit(0);
|
||||||
const double_bigit lower = value & mask;
|
const double_bigit lower = value & mask;
|
||||||
const double_bigit upper = value >> bigit_bits;
|
const double_bigit upper = value >> bigit_bits;
|
||||||
@ -430,14 +443,16 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bigint() : exp_(0) {}
|
FMT_CONSTEXPR20 bigint() : exp_(0) {}
|
||||||
explicit bigint(uint64_t n) { assign(n); }
|
explicit bigint(uint64_t n) { assign(n); }
|
||||||
~bigint() { FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); }
|
FMT_CONSTEXPR20 ~bigint() {
|
||||||
|
FMT_ASSERT(bigits_.capacity() <= bigits_capacity, "");
|
||||||
|
}
|
||||||
|
|
||||||
bigint(const bigint&) = delete;
|
bigint(const bigint&) = delete;
|
||||||
void operator=(const bigint&) = delete;
|
void operator=(const bigint&) = delete;
|
||||||
|
|
||||||
void assign(const bigint& other) {
|
FMT_CONSTEXPR20 void assign(const bigint& other) {
|
||||||
auto size = other.bigits_.size();
|
auto size = other.bigits_.size();
|
||||||
bigits_.resize(size);
|
bigits_.resize(size);
|
||||||
auto data = other.bigits_.data();
|
auto data = other.bigits_.data();
|
||||||
@ -445,7 +460,7 @@ class bigint {
|
|||||||
exp_ = other.exp_;
|
exp_ = other.exp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign(uint64_t n) {
|
FMT_CONSTEXPR20 void assign(uint64_t n) {
|
||||||
size_t num_bigits = 0;
|
size_t num_bigits = 0;
|
||||||
do {
|
do {
|
||||||
bigits_[num_bigits++] = n & ~bigit(0);
|
bigits_[num_bigits++] = n & ~bigit(0);
|
||||||
@ -455,9 +470,11 @@ class bigint {
|
|||||||
exp_ = 0;
|
exp_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
FMT_CONSTEXPR20 int num_bigits() const {
|
||||||
|
return static_cast<int>(bigits_.size()) + exp_;
|
||||||
|
}
|
||||||
|
|
||||||
FMT_NOINLINE bigint& operator<<=(int shift) {
|
FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
|
||||||
FMT_ASSERT(shift >= 0, "");
|
FMT_ASSERT(shift >= 0, "");
|
||||||
exp_ += shift / bigit_bits;
|
exp_ += shift / bigit_bits;
|
||||||
shift %= bigit_bits;
|
shift %= bigit_bits;
|
||||||
@ -472,13 +489,13 @@ class bigint {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Int> bigint& operator*=(Int value) {
|
template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
|
||||||
FMT_ASSERT(value > 0, "");
|
FMT_ASSERT(value > 0, "");
|
||||||
multiply(uint32_or_64_or_128_t<Int>(value));
|
multiply(uint32_or_64_or_128_t<Int>(value));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend int compare(const bigint& lhs, const bigint& rhs) {
|
friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
|
||||||
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
||||||
if (num_lhs_bigits != num_rhs_bigits)
|
if (num_lhs_bigits != num_rhs_bigits)
|
||||||
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
||||||
@ -495,8 +512,8 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns compare(lhs1 + lhs2, rhs).
|
// Returns compare(lhs1 + lhs2, rhs).
|
||||||
friend int add_compare(const bigint& lhs1, const bigint& lhs2,
|
friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
|
||||||
const bigint& rhs) {
|
const bigint& rhs) {
|
||||||
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
||||||
int num_rhs_bigits = rhs.num_bigits();
|
int num_rhs_bigits = rhs.num_bigits();
|
||||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||||
@ -519,7 +536,7 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assigns pow(10, exp) to this bigint.
|
// Assigns pow(10, exp) to this bigint.
|
||||||
void assign_pow10(int exp) {
|
FMT_CONSTEXPR20 void assign_pow10(int exp) {
|
||||||
FMT_ASSERT(exp >= 0, "");
|
FMT_ASSERT(exp >= 0, "");
|
||||||
if (exp == 0) return assign(1);
|
if (exp == 0) return assign(1);
|
||||||
// Find the top bit.
|
// Find the top bit.
|
||||||
@ -538,7 +555,7 @@ class bigint {
|
|||||||
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
||||||
}
|
}
|
||||||
|
|
||||||
void square() {
|
FMT_CONSTEXPR20 void square() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
int num_result_bigits = 2 * num_bigits;
|
int num_result_bigits = 2 * num_bigits;
|
||||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||||
@ -569,7 +586,7 @@ class bigint {
|
|||||||
|
|
||||||
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
||||||
// exponents equal. This simplifies some operations such as subtraction.
|
// exponents equal. This simplifies some operations such as subtraction.
|
||||||
void align(const bigint& other) {
|
FMT_CONSTEXPR20 void align(const bigint& other) {
|
||||||
int exp_difference = exp_ - other.exp_;
|
int exp_difference = exp_ - other.exp_;
|
||||||
if (exp_difference <= 0) return;
|
if (exp_difference <= 0) return;
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
@ -582,7 +599,7 @@ class bigint {
|
|||||||
|
|
||||||
// Divides this bignum by divisor, assigning the remainder to this and
|
// Divides this bignum by divisor, assigning the remainder to this and
|
||||||
// returning the quotient.
|
// returning the quotient.
|
||||||
int divmod_assign(const bigint& divisor) {
|
FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
|
||||||
FMT_ASSERT(this != &divisor, "");
|
FMT_ASSERT(this != &divisor, "");
|
||||||
if (compare(*this, divisor) < 0) return 0;
|
if (compare(*this, divisor) < 0) return 0;
|
||||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||||
@ -602,8 +619,9 @@ enum class round_direction { unknown, up, down };
|
|||||||
// some number v and the error, returns whether v should be rounded up, down, or
|
// some number v and the error, returns whether v should be rounded up, down, or
|
||||||
// whether the rounding direction can't be determined due to error.
|
// whether the rounding direction can't be determined due to error.
|
||||||
// error should be less than divisor / 2.
|
// error should be less than divisor / 2.
|
||||||
inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
|
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
|
||||||
uint64_t error) {
|
uint64_t remainder,
|
||||||
|
uint64_t error) {
|
||||||
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
||||||
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
||||||
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
||||||
@ -626,19 +644,52 @@ enum result {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t power_of_10_64(int exp) {
|
struct gen_digits_handler {
|
||||||
static constexpr const uint64_t data[] = {1, FMT_POWERS_OF_10(1),
|
char* buf;
|
||||||
FMT_POWERS_OF_10(1000000000ULL),
|
int size;
|
||||||
10000000000000000000ULL};
|
int precision;
|
||||||
return data[exp];
|
int exp10;
|
||||||
}
|
bool fixed;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
|
||||||
|
uint64_t remainder, uint64_t error,
|
||||||
|
bool integral) {
|
||||||
|
FMT_ASSERT(remainder < divisor, "");
|
||||||
|
buf[size++] = digit;
|
||||||
|
if (!integral && error >= remainder) return digits::error;
|
||||||
|
if (size < precision) return digits::more;
|
||||||
|
if (!integral) {
|
||||||
|
// Check if error * 2 < divisor with overflow prevention.
|
||||||
|
// The check is not needed for the integral part because error = 1
|
||||||
|
// and divisor > (1 << 32) there.
|
||||||
|
if (error >= divisor || error >= divisor - error) return digits::error;
|
||||||
|
} else {
|
||||||
|
FMT_ASSERT(error == 1 && divisor > 2, "");
|
||||||
|
}
|
||||||
|
auto dir = get_round_direction(divisor, remainder, error);
|
||||||
|
if (dir != round_direction::up)
|
||||||
|
return dir == round_direction::down ? digits::done : digits::error;
|
||||||
|
++buf[size - 1];
|
||||||
|
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
||||||
|
buf[i] = '0';
|
||||||
|
++buf[i - 1];
|
||||||
|
}
|
||||||
|
if (buf[0] > '9') {
|
||||||
|
buf[0] = '1';
|
||||||
|
if (fixed)
|
||||||
|
buf[size++] = '0';
|
||||||
|
else
|
||||||
|
++exp10;
|
||||||
|
}
|
||||||
|
return digits::done;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Generates output using the Grisu digit-gen algorithm.
|
// Generates output using the Grisu digit-gen algorithm.
|
||||||
// error: the size of the region (lower, upper) outside of which numbers
|
// error: the size of the region (lower, upper) outside of which numbers
|
||||||
// definitely do not round to value (Delta in Grisu3).
|
// definitely do not round to value (Delta in Grisu3).
|
||||||
template <typename Handler>
|
FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits(
|
||||||
FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
fp value, uint64_t error, int& exp, gen_digits_handler& handler) {
|
||||||
Handler& handler) {
|
|
||||||
const fp one(1ULL << -value.e, value.e);
|
const fp one(1ULL << -value.e, value.e);
|
||||||
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
// 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
|
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
||||||
@ -649,10 +700,28 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
|||||||
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
// The fractional part of scaled value (p2 in Grisu) c = value % one.
|
||||||
uint64_t fractional = value.f & (one.f - 1);
|
uint64_t fractional = value.f & (one.f - 1);
|
||||||
exp = count_digits(integral); // kappa in Grisu.
|
exp = count_digits(integral); // kappa in Grisu.
|
||||||
// Divide by 10 to prevent overflow.
|
// Non-fixed formats require at least one digit and no precision adjustment.
|
||||||
auto result = handler.on_start(power_of_10_64(exp - 1) << -one.e,
|
if (handler.fixed) {
|
||||||
value.f / 10, error * 10, exp);
|
// Adjust fixed precision by exponent because it is relative to decimal
|
||||||
if (result != digits::more) return result;
|
// point.
|
||||||
|
int precision_offset = exp + handler.exp10;
|
||||||
|
if (precision_offset > 0 &&
|
||||||
|
handler.precision > max_value<int>() - precision_offset) {
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
}
|
||||||
|
handler.precision += precision_offset;
|
||||||
|
// Check if precision is satisfied just by leading zeros, e.g.
|
||||||
|
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
|
||||||
|
if (handler.precision <= 0) {
|
||||||
|
if (handler.precision < 0) return digits::done;
|
||||||
|
// Divide by 10 to prevent overflow.
|
||||||
|
uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e;
|
||||||
|
auto dir = get_round_direction(divisor, value.f / 10, error * 10);
|
||||||
|
if (dir == round_direction::unknown) return digits::error;
|
||||||
|
handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
|
||||||
|
return digits::done;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Generate digits for the integral part. This can produce up to 10 digits.
|
// Generate digits for the integral part. This can produce up to 10 digits.
|
||||||
do {
|
do {
|
||||||
uint32_t digit = 0;
|
uint32_t digit = 0;
|
||||||
@ -699,9 +768,9 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
|||||||
}
|
}
|
||||||
--exp;
|
--exp;
|
||||||
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
||||||
result = handler.on_digit(static_cast<char>('0' + digit),
|
auto result = handler.on_digit(static_cast<char>('0' + digit),
|
||||||
power_of_10_64(exp) << -one.e, remainder, error,
|
impl_data::power_of_10_64[exp] << -one.e,
|
||||||
exp, true);
|
remainder, error, true);
|
||||||
if (result != digits::more) return result;
|
if (result != digits::more) return result;
|
||||||
} while (exp > 0);
|
} while (exp > 0);
|
||||||
// Generate digits for the fractional part.
|
// Generate digits for the fractional part.
|
||||||
@ -711,69 +780,11 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
|||||||
char digit = static_cast<char>('0' + (fractional >> -one.e));
|
char digit = static_cast<char>('0' + (fractional >> -one.e));
|
||||||
fractional &= one.f - 1;
|
fractional &= one.f - 1;
|
||||||
--exp;
|
--exp;
|
||||||
result = handler.on_digit(digit, one.f, fractional, error, exp, false);
|
auto result = handler.on_digit(digit, one.f, fractional, error, false);
|
||||||
if (result != digits::more) return result;
|
if (result != digits::more) return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fixed precision digit handler.
|
|
||||||
struct fixed_handler {
|
|
||||||
char* buf;
|
|
||||||
int size;
|
|
||||||
int precision;
|
|
||||||
int exp10;
|
|
||||||
bool fixed;
|
|
||||||
|
|
||||||
digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error,
|
|
||||||
int& exp) {
|
|
||||||
// Non-fixed formats require at least one digit and no precision adjustment.
|
|
||||||
if (!fixed) return digits::more;
|
|
||||||
// Adjust fixed precision by exponent because it is relative to decimal
|
|
||||||
// point.
|
|
||||||
precision += exp + exp10;
|
|
||||||
// Check if precision is satisfied just by leading zeros, e.g.
|
|
||||||
// format("{:.2f}", 0.001) gives "0.00" without generating any digits.
|
|
||||||
if (precision > 0) return digits::more;
|
|
||||||
if (precision < 0) return digits::done;
|
|
||||||
auto dir = get_round_direction(divisor, remainder, error);
|
|
||||||
if (dir == round_direction::unknown) return digits::error;
|
|
||||||
buf[size++] = dir == round_direction::up ? '1' : '0';
|
|
||||||
return digits::done;
|
|
||||||
}
|
|
||||||
|
|
||||||
digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
|
|
||||||
uint64_t error, int, bool integral) {
|
|
||||||
FMT_ASSERT(remainder < divisor, "");
|
|
||||||
buf[size++] = digit;
|
|
||||||
if (!integral && error >= remainder) return digits::error;
|
|
||||||
if (size < precision) return digits::more;
|
|
||||||
if (!integral) {
|
|
||||||
// Check if error * 2 < divisor with overflow prevention.
|
|
||||||
// The check is not needed for the integral part because error = 1
|
|
||||||
// and divisor > (1 << 32) there.
|
|
||||||
if (error >= divisor || error >= divisor - error) return digits::error;
|
|
||||||
} else {
|
|
||||||
FMT_ASSERT(error == 1 && divisor > 2, "");
|
|
||||||
}
|
|
||||||
auto dir = get_round_direction(divisor, remainder, error);
|
|
||||||
if (dir != round_direction::up)
|
|
||||||
return dir == round_direction::down ? digits::done : digits::error;
|
|
||||||
++buf[size - 1];
|
|
||||||
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
|
|
||||||
buf[i] = '0';
|
|
||||||
++buf[i - 1];
|
|
||||||
}
|
|
||||||
if (buf[0] > '9') {
|
|
||||||
buf[0] = '1';
|
|
||||||
if (fixed)
|
|
||||||
buf[size++] = '0';
|
|
||||||
else
|
|
||||||
++exp10;
|
|
||||||
}
|
|
||||||
return digits::done;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A 128-bit integer type used internally,
|
// A 128-bit integer type used internally,
|
||||||
struct uint128_wrapper {
|
struct uint128_wrapper {
|
||||||
uint128_wrapper() = default;
|
uint128_wrapper() = default;
|
||||||
@ -897,8 +908,7 @@ inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT {
|
|||||||
inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
|
inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
||||||
const int shift = 22;
|
const int shift = 22;
|
||||||
return (e * static_cast<int>(data::log10_2_significand >> (64 - shift))) >>
|
return (e * static_cast<int>(log10_2_significand >> (64 - shift))) >> shift;
|
||||||
shift;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Various fast log computations.
|
// Various fast log computations.
|
||||||
@ -916,8 +926,7 @@ inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT {
|
|||||||
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
|
||||||
const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375;
|
const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375;
|
||||||
const int shift_amount = 22;
|
const int shift_amount = 22;
|
||||||
return (e * static_cast<int>(data::log10_2_significand >>
|
return (e * static_cast<int>(log10_2_significand >> (64 - shift_amount)) -
|
||||||
(64 - shift_amount)) -
|
|
||||||
static_cast<int>(log10_4_over_3_fractional_digits >>
|
static_cast<int>(log10_4_over_3_fractional_digits >>
|
||||||
(64 - shift_amount))) >>
|
(64 - shift_amount))) >>
|
||||||
shift_amount;
|
shift_amount;
|
||||||
@ -1042,7 +1051,7 @@ template <> struct cache_accessor<float> {
|
|||||||
static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
|
static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
|
||||||
"k is out of range");
|
"k is out of range");
|
||||||
constexpr const uint64_t pow10_significands[] = {
|
static constexpr const uint64_t pow10_significands[] = {
|
||||||
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
|
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
|
||||||
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
|
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
|
||||||
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
|
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
|
||||||
@ -2210,24 +2219,21 @@ small_divisor_case_label:
|
|||||||
}
|
}
|
||||||
} // namespace dragonbox
|
} // namespace dragonbox
|
||||||
|
|
||||||
// Formats value using a variation of the Fixed-Precision Positive
|
// Formats a floating-point number using a variation of the Fixed-Precision
|
||||||
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||||
// https://fmt.dev/papers/p372-steele.pdf.
|
// https://fmt.dev/papers/p372-steele.pdf.
|
||||||
template <typename Double>
|
FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer,
|
||||||
void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
int num_digits, buffer<char>& buf,
|
||||||
int& exp10) {
|
int& exp10) {
|
||||||
bigint numerator; // 2 * R in (FPP)^2.
|
bigint numerator; // 2 * R in (FPP)^2.
|
||||||
bigint denominator; // 2 * S in (FPP)^2.
|
bigint denominator; // 2 * S in (FPP)^2.
|
||||||
// lower and upper are differences between value and corresponding boundaries.
|
// lower and upper are differences between value and corresponding boundaries.
|
||||||
bigint lower; // (M^- in (FPP)^2).
|
bigint lower; // (M^- in (FPP)^2).
|
||||||
bigint upper_store; // upper's value if different from lower.
|
bigint upper_store; // upper's value if different from lower.
|
||||||
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
bigint* upper = nullptr; // (M^+ in (FPP)^2).
|
||||||
fp value;
|
|
||||||
// Shift numerator and denominator by an extra bit or two (if lower boundary
|
// Shift numerator and denominator by an extra bit or two (if lower boundary
|
||||||
// is closer) to make lower and upper integers. This eliminates multiplication
|
// is closer) to make lower and upper integers. This eliminates multiplication
|
||||||
// by 2 during later computations.
|
// by 2 during later computations.
|
||||||
const bool is_predecessor_closer =
|
|
||||||
binary32 ? value.assign(static_cast<float>(d)) : value.assign(d);
|
|
||||||
int shift = is_predecessor_closer ? 2 : 1;
|
int shift = is_predecessor_closer ? 2 : 1;
|
||||||
uint64_t significand = value.f << shift;
|
uint64_t significand = value.f << shift;
|
||||||
if (value.e >= 0) {
|
if (value.e >= 0) {
|
||||||
@ -2297,9 +2303,9 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
|||||||
// Generate the given number of digits.
|
// Generate the given number of digits.
|
||||||
exp10 -= num_digits - 1;
|
exp10 -= num_digits - 1;
|
||||||
if (num_digits == 0) {
|
if (num_digits == 0) {
|
||||||
buf.try_resize(1);
|
|
||||||
denominator *= 10;
|
denominator *= 10;
|
||||||
buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
||||||
|
buf.push_back(digit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buf.try_resize(to_unsigned(num_digits));
|
buf.try_resize(to_unsigned(num_digits));
|
||||||
@ -2330,9 +2336,12 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
|||||||
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename Float>
|
||||||
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision,
|
||||||
static_assert(!std::is_same<T, float>::value, "");
|
float_specs specs,
|
||||||
|
buffer<char>& buf) {
|
||||||
|
// float is passed as double to reduce the number of instantiations.
|
||||||
|
static_assert(!std::is_same<Float, float>::value, "");
|
||||||
FMT_ASSERT(value >= 0, "value is negative");
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
|
||||||
const bool fixed = specs.format == float_format::fixed;
|
const bool fixed = specs.format == float_format::fixed;
|
||||||
@ -2342,13 +2351,13 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
buf.try_resize(to_unsigned(precision));
|
buf.try_resize(to_unsigned(precision));
|
||||||
std::uninitialized_fill_n(buf.data(), precision, '0');
|
fill_n(buf.data(), precision, '0');
|
||||||
return -precision;
|
return -precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
|
if (specs.fallback) return snprintf_float(value, precision, specs, buf);
|
||||||
|
|
||||||
if (precision < 0) {
|
if (!is_constant_evaluated() && precision < 0) {
|
||||||
// Use Dragonbox for the shortest format.
|
// Use Dragonbox for the shortest format.
|
||||||
if (specs.binary32) {
|
if (specs.binary32) {
|
||||||
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
||||||
@ -2360,26 +2369,37 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
return dec.exponent;
|
return dec.exponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Grisu + Dragon4 for the given precision:
|
|
||||||
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
|
|
||||||
int exp = 0;
|
int exp = 0;
|
||||||
const int min_exp = -60; // alpha in Grisu.
|
bool use_dragon = true;
|
||||||
int cached_exp10 = 0; // K in Grisu.
|
if (is_fast_float<Float>()) {
|
||||||
fp normalized = normalize(fp(value));
|
// Use Grisu + Dragon4 for the given precision:
|
||||||
const auto cached_pow = get_cached_power(
|
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
|
||||||
min_exp - (normalized.e + fp::significand_size), cached_exp10);
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
normalized = normalized * cached_pow;
|
int cached_exp10 = 0; // K in Grisu.
|
||||||
// Limit precision to the maximum possible number of significant digits in an
|
fp normalized = normalize(fp(value));
|
||||||
// IEEE754 double because we don't need to generate zeros.
|
const auto cached_pow = get_cached_power(
|
||||||
const int max_double_digits = 767;
|
min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
|
||||||
if (precision > max_double_digits) precision = max_double_digits;
|
normalized = normalized * cached_pow;
|
||||||
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||||
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) {
|
if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
|
||||||
exp += handler.size - cached_exp10 - 1;
|
!is_constant_evaluated()) {
|
||||||
fallback_format(value, handler.precision, specs.binary32, buf, exp);
|
exp += handler.exp10;
|
||||||
} else {
|
buf.try_resize(to_unsigned(handler.size));
|
||||||
exp += handler.exp10;
|
use_dragon = false;
|
||||||
buf.try_resize(to_unsigned(handler.size));
|
} else {
|
||||||
|
exp += handler.size - cached_exp10 - 1;
|
||||||
|
precision = handler.precision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (use_dragon) {
|
||||||
|
auto f = fp();
|
||||||
|
bool is_predecessor_closer =
|
||||||
|
specs.binary32 ? f.assign(static_cast<float>(value)) : f.assign(value);
|
||||||
|
// Limit precision to the maximum possible number of significant digits in
|
||||||
|
// an IEEE754 double because we don't need to generate zeros.
|
||||||
|
const int max_double_digits = 767;
|
||||||
|
if (precision > max_double_digits) precision = max_double_digits;
|
||||||
|
format_dragon(f, is_predecessor_closer, precision, buf, exp);
|
||||||
}
|
}
|
||||||
if (!fixed && !specs.showpoint) {
|
if (!fixed && !specs.showpoint) {
|
||||||
// Remove trailing zeros.
|
// Remove trailing zeros.
|
||||||
@ -2391,7 +2411,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
buf.try_resize(num_digits);
|
buf.try_resize(num_digits);
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
} // namespace detail
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int snprintf_float(T value, int precision, float_specs specs,
|
int snprintf_float(T value, int precision, float_specs specs,
|
||||||
@ -2525,8 +2545,8 @@ template <> struct formatter<detail::bigint> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
for_each_codepoint(s, [this](uint32_t cp, int error) {
|
for_each_codepoint(s, [this](uint32_t cp, string_view) {
|
||||||
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
|
if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
|
||||||
if (cp <= 0xFFFF) {
|
if (cp <= 0xFFFF) {
|
||||||
buffer_.push_back(static_cast<wchar_t>(cp));
|
buffer_.push_back(static_cast<wchar_t>(cp));
|
||||||
} else {
|
} else {
|
||||||
@ -2534,6 +2554,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
|||||||
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
|
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
|
||||||
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
|
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
buffer_.push_back(0);
|
buffer_.push_back(0);
|
||||||
}
|
}
|
||||||
@ -2549,15 +2570,17 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
|
|||||||
format_error_code(out, error_code, message);
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void detail::error_handler::on_error(const char* message) {
|
|
||||||
FMT_THROW(format_error(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void report_system_error(int error_code,
|
FMT_FUNC void report_system_error(int error_code,
|
||||||
const char* message) FMT_NOEXCEPT {
|
const char* message) FMT_NOEXCEPT {
|
||||||
report_error(format_system_error, error_code, message);
|
report_error(format_system_error, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
// This function is defined here and not inline for ABI compatiblity.
|
||||||
|
FMT_FUNC void detail::error_handler::on_error(const char* message) {
|
||||||
|
throw_format_error(message);
|
||||||
|
}
|
||||||
|
|
||||||
FMT_FUNC std::string vformat(string_view fmt, format_args args) {
|
FMT_FUNC std::string vformat(string_view fmt, format_args args) {
|
||||||
// Don't optimize the "{}" case to keep the binary size small and because it
|
// Don't optimize the "{}" case to keep the binary size small and because it
|
||||||
// can be better optimized in fmt::format anyway.
|
// can be better optimized in fmt::format anyway.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,17 +21,20 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_USE_FCNTL
|
||||||
// UWP doesn't provide _pipe.
|
// UWP doesn't provide _pipe.
|
||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
# endif
|
||||||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
defined(__linux__)) && \
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || \
|
||||||
# include <fcntl.h> // for O_RDONLY
|
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# define FMT_USE_FCNTL 1
|
# include <fcntl.h> // for O_RDONLY
|
||||||
#else
|
# define FMT_USE_FCNTL 1
|
||||||
# define FMT_USE_FCNTL 0
|
# else
|
||||||
|
# define FMT_USE_FCNTL 0
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_POSIX
|
#ifndef FMT_POSIX
|
||||||
@ -390,23 +393,26 @@ struct ostream_params {
|
|||||||
: ostream_params(params...) {
|
: ostream_params(params...) {
|
||||||
this->buffer_size = bs.value;
|
this->buffer_size = bs.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intel has a bug that results in failure to deduce a constructor
|
||||||
|
// for empty parameter packs.
|
||||||
|
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||||
|
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||||
|
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||||
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_DETAIL_NAMESPACE
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
constexpr detail::buffer_size buffer_size;
|
// Added {} below to work around default constructor error known to
|
||||||
|
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||||
|
constexpr detail::buffer_size buffer_size{};
|
||||||
|
|
||||||
/** A fast output stream which is not thread-safe. */
|
/** A fast output stream which is not thread-safe. */
|
||||||
class FMT_API ostream final : private detail::buffer<char> {
|
class FMT_API ostream final : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
void flush() {
|
|
||||||
if (size() == 0) return;
|
|
||||||
file_.write(data(), size());
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void grow(size_t) override;
|
void grow(size_t) override;
|
||||||
|
|
||||||
ostream(cstring_view path, const detail::ostream_params& params)
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
@ -426,6 +432,12 @@ class FMT_API ostream final : private detail::buffer<char> {
|
|||||||
delete[] data();
|
delete[] data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void flush() {
|
||||||
|
if (size() == 0) return;
|
||||||
|
file_.write(data(), size());
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
friend ostream output_file(cstring_view path, T... params);
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
@ -500,7 +512,7 @@ class locale {
|
|||||||
|
|
||||||
// Converts string to floating-point number and advances str past the end
|
// Converts string to floating-point number and advances str past the end
|
||||||
// of the parsed input.
|
// of the parsed input.
|
||||||
double strtod(const char*& str) const {
|
FMT_DEPRECATED double strtod(const char*& str) const {
|
||||||
char* end = nullptr;
|
char* end = nullptr;
|
||||||
double result = strtod_l(str, &end, locale_);
|
double result = strtod_l(str, &end, locale_);
|
||||||
str = end;
|
str = end;
|
||||||
|
@ -14,73 +14,20 @@
|
|||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> class basic_printf_parse_context;
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
// Checks if T has a user-defined operator<<.
|
||||||
private:
|
template <typename T, typename Char, typename Enable = void>
|
||||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
class is_streamable {
|
||||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
|
||||||
|
|
||||||
buffer<Char>& buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The put-area is actually always empty. This makes the implementation
|
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
|
||||||
// results in a call to xsputn.
|
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
|
||||||
buffer_.append(s, s + count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct converter {
|
|
||||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
|
||||||
private:
|
|
||||||
void_t<> operator<<(converter);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hide insertion operators for built-in types.
|
|
||||||
template <typename Char, typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
|
||||||
template <typename Char, typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
|
||||||
// std::ostream).
|
|
||||||
template <typename T, typename Char> class is_streamable {
|
|
||||||
private:
|
private:
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
static auto test(int)
|
||||||
<< std::declval<U>()),
|
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||||
void_t<>>::value>
|
<< std::declval<U>()) != 0>;
|
||||||
test(int);
|
|
||||||
|
|
||||||
template <typename> static std::false_type test(...);
|
template <typename> static auto test(...) -> std::false_type;
|
||||||
|
|
||||||
using result = decltype(test<T>(0));
|
using result = decltype(test<T>(0));
|
||||||
|
|
||||||
@ -90,7 +37,21 @@ template <typename T, typename Char> class is_streamable {
|
|||||||
static const bool value = result::value;
|
static const bool value = result::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Formatting of built-in types and arrays is intentionally disabled because
|
||||||
|
// it's handled by standard (non-ostream) formatters.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct is_streamable<
|
||||||
|
T, Char,
|
||||||
|
enable_if_t<
|
||||||
|
std::is_arithmetic<T>::value || std::is_array<T>::value ||
|
||||||
|
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
|
||||||
|
std::is_same<T, std::basic_string<Char>>::value ||
|
||||||
|
std::is_same<T, std_string_view<Char>>::value ||
|
||||||
|
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
// Write the content of buf to os.
|
// Write the content of buf to os.
|
||||||
|
// It is a separate function rather than a part of vprint to simplify testing.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
const Char* buf_data = buf.data();
|
const Char* buf_data = buf.data();
|
||||||
@ -108,8 +69,8 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
|||||||
template <typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void format_value(buffer<Char>& buf, const T& value,
|
void format_value(buffer<Char>& buf, const T& value,
|
||||||
locale_ref loc = locale_ref()) {
|
locale_ref loc = locale_ref()) {
|
||||||
formatbuf<Char> format_buf(buf);
|
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
#endif
|
#endif
|
||||||
@ -122,29 +83,22 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
: private formatter<basic_string_view<Char>, Char> {
|
: private formatter<basic_string_view<Char>, Char> {
|
||||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
using formatter<basic_string_view<Char>, Char>::parse;
|
||||||
-> decltype(ctx.begin()) {
|
|
||||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
|
||||||
}
|
|
||||||
template <typename ParseCtx,
|
|
||||||
FMT_ENABLE_IF(std::is_same<
|
|
||||||
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
|
||||||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
return formatter<basic_string_view<Char>, Char>::format(
|
||||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
{buffer.data(), buffer.size()}, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||||
}
|
}
|
||||||
@ -155,7 +109,7 @@ FMT_MODULE_EXPORT
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
detail::vformat_to(buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args);
|
||||||
detail::write_buffer(os, buffer);
|
detail::write_buffer(os, buffer);
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
|||||||
|
|
||||||
OutputIt write_null_pointer(bool is_string = false) {
|
OutputIt write_null_pointer(bool is_string = false) {
|
||||||
auto s = this->specs;
|
auto s = this->specs;
|
||||||
s.type = 0;
|
s.type = presentation_type::none;
|
||||||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,8 +249,10 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
|||||||
// std::is_same instead.
|
// std::is_same instead.
|
||||||
if (std::is_same<T, Char>::value) {
|
if (std::is_same<T, Char>::value) {
|
||||||
format_specs fmt_specs = this->specs;
|
format_specs fmt_specs = this->specs;
|
||||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
if (fmt_specs.type != presentation_type::none &&
|
||||||
|
fmt_specs.type != presentation_type::chr) {
|
||||||
return (*this)(static_cast<int>(value));
|
return (*this)(static_cast<int>(value));
|
||||||
|
}
|
||||||
fmt_specs.sign = sign::none;
|
fmt_specs.sign = sign::none;
|
||||||
fmt_specs.alt = false;
|
fmt_specs.alt = false;
|
||||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||||
@ -271,13 +273,13 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
|||||||
/** Formats a null-terminated C string. */
|
/** Formats a null-terminated C string. */
|
||||||
OutputIt operator()(const char* value) {
|
OutputIt operator()(const char* value) {
|
||||||
if (value) return base::operator()(value);
|
if (value) return base::operator()(value);
|
||||||
return write_null_pointer(this->specs.type != 'p');
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a null-terminated wide C string. */
|
/** Formats a null-terminated wide C string. */
|
||||||
OutputIt operator()(const wchar_t* value) {
|
OutputIt operator()(const wchar_t* value) {
|
||||||
if (value) return base::operator()(value);
|
if (value) return base::operator()(value);
|
||||||
return write_null_pointer(this->specs.type != 'p');
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputIt operator()(basic_string_view<Char> value) {
|
OutputIt operator()(basic_string_view<Char> value) {
|
||||||
@ -490,13 +492,13 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
|
|
||||||
// Parse type.
|
// Parse type.
|
||||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||||
specs.type = static_cast<char>(*it++);
|
char type = static_cast<char>(*it++);
|
||||||
if (arg.is_integral()) {
|
if (arg.is_integral()) {
|
||||||
// Normalize type.
|
// Normalize type.
|
||||||
switch (specs.type) {
|
switch (type) {
|
||||||
case 'i':
|
case 'i':
|
||||||
case 'u':
|
case 'u':
|
||||||
specs.type = 'd';
|
type = 'd';
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
visit_format_arg(
|
visit_format_arg(
|
||||||
@ -505,6 +507,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
specs.type = parse_presentation_type(type);
|
||||||
|
if (specs.type == presentation_type::none)
|
||||||
|
parse_ctx.on_error("invalid type specifier");
|
||||||
|
|
||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
|
@ -13,37 +13,13 @@
|
|||||||
#define FMT_RANGES_H_
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char, typename Enable = void> struct formatting_range {
|
|
||||||
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
|
||||||
Char prefix = '{';
|
|
||||||
Char postfix = '}';
|
|
||||||
#else
|
|
||||||
Char prefix = '[';
|
|
||||||
Char postfix = ']';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void> struct formatting_tuple {
|
|
||||||
Char prefix = '(';
|
|
||||||
Char postfix = ')';
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename RangeT, typename OutputIterator>
|
template <typename RangeT, typename OutputIterator>
|
||||||
@ -71,7 +47,7 @@ OutputIterator copy(wchar_t ch, OutputIterator out) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true value if T has std::string interface, like std::string_view.
|
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||||
template <typename T> class is_std_string_like {
|
template <typename T> class is_std_string_like {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p)
|
static auto check(U* p)
|
||||||
@ -80,12 +56,40 @@ template <typename T> class is_std_string_like {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
is_string<T>::value ||
|
||||||
|
std::is_convertible<T, std_string_view<char>>::value ||
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> class is_map {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||||
|
#else
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class is_set {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||||
|
#else
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
template <typename... Ts> struct conditional_helper {};
|
template <typename... Ts> struct conditional_helper {};
|
||||||
|
|
||||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
@ -143,16 +147,16 @@ struct has_mutable_begin_end : std::false_type {};
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_const_begin_end<
|
struct has_const_begin_end<
|
||||||
T, void_t<decltype(detail::range_begin(
|
T,
|
||||||
std::declval<const remove_cvref_t<T>&>())),
|
void_t<
|
||||||
decltype(detail::range_begin(
|
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||||
std::declval<const remove_cvref_t<T>&>()))>>
|
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_mutable_begin_end<
|
struct has_mutable_begin_end<
|
||||||
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||||
decltype(detail::range_begin(std::declval<T>())),
|
decltype(detail::range_end(std::declval<T>())),
|
||||||
enable_if_t<std::is_copy_constructible<T>::value>>>
|
enable_if_t<std::is_copy_constructible<T>::value>>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
@ -160,34 +164,10 @@ template <typename T>
|
|||||||
struct is_range_<T, void>
|
struct is_range_<T, void>
|
||||||
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||||
has_mutable_begin_end<T>::value)> {};
|
has_mutable_begin_end<T>::value)> {};
|
||||||
|
|
||||||
template <typename T, typename Enable = void> struct range_to_view;
|
|
||||||
template <typename T>
|
|
||||||
struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
|
|
||||||
struct view_t {
|
|
||||||
const T* m_range_ptr;
|
|
||||||
|
|
||||||
auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
|
|
||||||
auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
|
|
||||||
};
|
|
||||||
static auto view(const T& range) -> view_t { return {&range}; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
|
|
||||||
has_mutable_begin_end<T>::value>> {
|
|
||||||
struct view_t {
|
|
||||||
T m_range_copy;
|
|
||||||
|
|
||||||
auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
|
|
||||||
auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
|
|
||||||
};
|
|
||||||
static auto view(const T& range) -> view_t { return {range}; }
|
|
||||||
};
|
|
||||||
# undef FMT_DECLTYPE_RETURN
|
# undef FMT_DECLTYPE_RETURN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// tuple_size and tuple_element check.
|
// tuple_size and tuple_element check.
|
||||||
template <typename T> class is_tuple_like_ {
|
template <typename T> class is_tuple_like_ {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
@ -251,16 +231,295 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
struct singleton {
|
||||||
typename Char, typename OutputIt, typename Arg,
|
unsigned char upper;
|
||||||
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
|
unsigned char lower_count;
|
||||||
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
};
|
||||||
|
|
||||||
|
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||||
|
size_t singletons_size,
|
||||||
|
const unsigned char* singleton_lowers,
|
||||||
|
const unsigned char* normal, size_t normal_size)
|
||||||
|
-> bool {
|
||||||
|
auto upper = x >> 8;
|
||||||
|
auto lower_start = 0;
|
||||||
|
for (size_t i = 0; i < singletons_size; ++i) {
|
||||||
|
auto s = singletons[i];
|
||||||
|
auto lower_end = lower_start + s.lower_count;
|
||||||
|
if (upper < s.upper) break;
|
||||||
|
if (upper == s.upper) {
|
||||||
|
for (auto j = lower_start; j < lower_end; ++j) {
|
||||||
|
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lower_start = lower_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xsigned = static_cast<int>(x);
|
||||||
|
auto current = true;
|
||||||
|
for (size_t i = 0; i < normal_size; ++i) {
|
||||||
|
auto v = static_cast<int>(normal[i]);
|
||||||
|
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
|
||||||
|
xsigned -= len;
|
||||||
|
if (xsigned < 0) break;
|
||||||
|
current = !current;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true iff the code point cp is printable.
|
||||||
|
// This code is generated by support/printable.py.
|
||||||
|
inline auto is_printable(uint32_t cp) -> bool {
|
||||||
|
static constexpr singleton singletons0[] = {
|
||||||
|
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
|
||||||
|
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
|
||||||
|
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
|
||||||
|
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
|
||||||
|
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
|
||||||
|
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
|
||||||
|
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
|
||||||
|
};
|
||||||
|
static constexpr unsigned char singletons0_lower[] = {
|
||||||
|
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
|
||||||
|
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
|
||||||
|
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
|
||||||
|
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
|
||||||
|
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
|
||||||
|
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
|
||||||
|
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||||
|
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
|
||||||
|
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
|
||||||
|
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
|
||||||
|
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
|
||||||
|
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
|
||||||
|
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
|
||||||
|
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
|
||||||
|
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
|
||||||
|
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
|
||||||
|
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
|
||||||
|
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
|
||||||
|
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
|
||||||
|
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
|
||||||
|
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
|
||||||
|
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
|
||||||
|
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
|
||||||
|
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
|
||||||
|
0xfe, 0xff,
|
||||||
|
};
|
||||||
|
static constexpr singleton singletons1[] = {
|
||||||
|
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
|
||||||
|
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
|
||||||
|
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
|
||||||
|
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
|
||||||
|
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
|
||||||
|
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
|
||||||
|
{0xfa, 2}, {0xfb, 1},
|
||||||
|
};
|
||||||
|
static constexpr unsigned char singletons1_lower[] = {
|
||||||
|
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
|
||||||
|
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
|
||||||
|
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
|
||||||
|
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||||
|
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
|
||||||
|
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
|
||||||
|
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
|
||||||
|
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
|
||||||
|
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
|
||||||
|
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
|
||||||
|
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
|
||||||
|
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
|
||||||
|
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
|
||||||
|
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
|
||||||
|
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
|
||||||
|
};
|
||||||
|
static constexpr unsigned char normal0[] = {
|
||||||
|
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
|
||||||
|
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
|
||||||
|
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
|
||||||
|
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
|
||||||
|
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
|
||||||
|
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
|
||||||
|
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
|
||||||
|
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
|
||||||
|
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
|
||||||
|
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
|
||||||
|
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
|
||||||
|
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
|
||||||
|
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
|
||||||
|
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
|
||||||
|
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
|
||||||
|
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
|
||||||
|
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
|
||||||
|
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
|
||||||
|
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
|
||||||
|
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
|
||||||
|
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
|
||||||
|
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
|
||||||
|
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
|
||||||
|
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
|
||||||
|
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
|
||||||
|
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
|
||||||
|
};
|
||||||
|
static constexpr unsigned char normal1[] = {
|
||||||
|
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
|
||||||
|
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
|
||||||
|
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
|
||||||
|
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
|
||||||
|
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
|
||||||
|
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
|
||||||
|
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
|
||||||
|
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
|
||||||
|
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
|
||||||
|
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
|
||||||
|
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
|
||||||
|
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
|
||||||
|
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
|
||||||
|
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
|
||||||
|
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
|
||||||
|
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
|
||||||
|
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
|
||||||
|
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
|
||||||
|
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
|
||||||
|
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
|
||||||
|
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
|
||||||
|
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
|
||||||
|
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
|
||||||
|
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
|
||||||
|
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
|
||||||
|
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
|
||||||
|
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
|
||||||
|
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
|
||||||
|
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
|
||||||
|
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
|
||||||
|
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
|
||||||
|
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
|
||||||
|
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
|
||||||
|
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
|
||||||
|
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
|
||||||
|
};
|
||||||
|
auto lower = static_cast<uint16_t>(cp);
|
||||||
|
if (cp < 0x10000) {
|
||||||
|
return is_printable(lower, singletons0,
|
||||||
|
sizeof(singletons0) / sizeof(*singletons0),
|
||||||
|
singletons0_lower, normal0, sizeof(normal0));
|
||||||
|
}
|
||||||
|
if (cp < 0x20000) {
|
||||||
|
return is_printable(lower, singletons1,
|
||||||
|
sizeof(singletons1) / sizeof(*singletons1),
|
||||||
|
singletons1_lower, normal1, sizeof(normal1));
|
||||||
|
}
|
||||||
|
if (0x2a6de <= cp && cp < 0x2a700) return false;
|
||||||
|
if (0x2b735 <= cp && cp < 0x2b740) return false;
|
||||||
|
if (0x2b81e <= cp && cp < 0x2b820) return false;
|
||||||
|
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
|
||||||
|
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
|
||||||
|
if (0x2fa1e <= cp && cp < 0x30000) return false;
|
||||||
|
if (0x3134b <= cp && cp < 0xe0100) return false;
|
||||||
|
if (0xe01f0 <= cp && cp < 0x110000) return false;
|
||||||
|
return cp < 0x110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto needs_escape(uint32_t cp) -> bool {
|
||||||
|
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
|
||||||
|
!is_printable(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct find_escape_result {
|
||||||
|
const Char* begin;
|
||||||
|
const Char* end;
|
||||||
|
uint32_t cp;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
auto find_escape(const Char* begin, const Char* end)
|
||||||
|
-> find_escape_result<Char> {
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
|
||||||
|
if (sizeof(Char) == 1 && cp >= 0x80) continue;
|
||||||
|
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||||
|
}
|
||||||
|
return {begin, nullptr, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto find_escape(const char* begin, const char* end)
|
||||||
|
-> find_escape_result<char> {
|
||||||
|
if (!is_utf8()) return find_escape<char>(begin, end);
|
||||||
|
auto result = find_escape_result<char>{end, nullptr, 0};
|
||||||
|
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
|
||||||
|
[&](uint32_t cp, string_view sv) {
|
||||||
|
if (needs_escape(cp)) {
|
||||||
|
result = {sv.begin(), sv.end(), cp};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||||
*out++ = '"';
|
*out++ = '"';
|
||||||
out = write<Char>(out, v);
|
auto begin = str.begin(), end = str.end();
|
||||||
|
do {
|
||||||
|
auto escape = find_escape(begin, end);
|
||||||
|
out = copy_str<Char>(begin, escape.begin, out);
|
||||||
|
begin = escape.end;
|
||||||
|
if (!begin) break;
|
||||||
|
auto c = static_cast<Char>(escape.cp);
|
||||||
|
switch (escape.cp) {
|
||||||
|
case '\n':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 'n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 'r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 't';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
FMT_FALLTHROUGH;
|
||||||
|
case '\\':
|
||||||
|
*out++ = '\\';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (is_utf8()) {
|
||||||
|
if (escape.cp < 0x100) {
|
||||||
|
out = format_to(out, "\\x{:02x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (escape.cp < 0x10000) {
|
||||||
|
out = format_to(out, "\\u{:04x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (escape.cp < 0x110000) {
|
||||||
|
out = format_to(out, "\\U{:08x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Char escape_char : basic_string_view<Char>(
|
||||||
|
escape.begin, to_unsigned(escape.end - escape.begin))) {
|
||||||
|
out = format_to(
|
||||||
|
out, "\\x{:02x}",
|
||||||
|
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*out++ = c;
|
||||||
|
} while (begin != end);
|
||||||
*out++ = '"';
|
*out++ = '"';
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename T,
|
||||||
|
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||||
|
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
||||||
|
auto sv = std_string_view<Char>(str);
|
||||||
|
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename OutputIt, typename Arg,
|
template <typename Char, typename OutputIt, typename Arg,
|
||||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||||
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||||
@ -288,43 +547,37 @@ template <typename T> struct is_tuple_like {
|
|||||||
template <typename TupleT, typename Char>
|
template <typename TupleT, typename Char>
|
||||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
private:
|
private:
|
||||||
// C++11 generic lambda for format()
|
// C++11 generic lambda for format().
|
||||||
template <typename FormatContext> struct format_each {
|
template <typename FormatContext> struct format_each {
|
||||||
template <typename T> void operator()(const T& v) {
|
template <typename T> void operator()(const T& v) {
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
out = detail::write_range_entry<Char>(out, v);
|
out = detail::write_range_entry<Char>(out, v);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
formatting_tuple<Char>& formatting;
|
int i;
|
||||||
size_t& i;
|
typename FormatContext::iterator& out;
|
||||||
typename std::add_lvalue_reference<
|
|
||||||
decltype(std::declval<FormatContext>().out())>::type out;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
formatting_tuple<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return formatting.parse(ctx);
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext = format_context>
|
template <typename FormatContext = format_context>
|
||||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
size_t i = 0;
|
*out++ = '(';
|
||||||
|
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||||
detail::copy(formatting.prefix, out);
|
*out++ = ')';
|
||||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
return out;
|
||||||
detail::copy(formatting.postfix, out);
|
|
||||||
|
|
||||||
return ctx.out();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||||
|
!detail::is_map<T>::value &&
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
@ -334,32 +587,80 @@ struct formatter<
|
|||||||
T, Char,
|
T, Char,
|
||||||
enable_if_t<
|
enable_if_t<
|
||||||
fmt::is_range<T, Char>::value
|
fmt::is_range<T, Char>::value
|
||||||
// Workaround a bug in MSVC 2017 and earlier.
|
// Workaround a bug in MSVC 2019 and earlier.
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
#if !FMT_MSC_VER
|
||||||
&& (has_formatter<detail::value_type<T>, format_context>::value ||
|
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||||
#endif
|
#endif
|
||||||
>> {
|
>> {
|
||||||
formatting_range<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return formatting.parse(ctx);
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <
|
||||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
typename FormatContext, typename U,
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
FMT_ENABLE_IF(
|
||||||
size_t i = 0;
|
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||||
auto view = detail::range_to_view<T>::view(values);
|
const T, T>>::value)>
|
||||||
auto it = view.begin();
|
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
auto end = view.end();
|
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
||||||
|
Char prefix = '{';
|
||||||
|
Char postfix = '}';
|
||||||
|
#else
|
||||||
|
Char prefix = detail::is_set<T>::value ? '{' : '[';
|
||||||
|
Char postfix = detail::is_set<T>::value ? '}' : ']';
|
||||||
|
#endif
|
||||||
|
auto out = ctx.out();
|
||||||
|
*out++ = prefix;
|
||||||
|
int i = 0;
|
||||||
|
auto it = std::begin(range);
|
||||||
|
auto end = std::end(range);
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
out = detail::write_range_entry<Char>(out, *it);
|
out = detail::write_range_entry<Char>(out, *it);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
return detail::copy(formatting.postfix, out);
|
*out++ = postfix;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
T, Char,
|
||||||
|
enable_if_t<
|
||||||
|
detail::is_map<T>::value
|
||||||
|
// Workaround a bug in MSVC 2019 and earlier.
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||||
|
#endif
|
||||||
|
>> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename FormatContext, typename U,
|
||||||
|
FMT_ENABLE_IF(
|
||||||
|
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||||
|
const T, T>>::value)>
|
||||||
|
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
*out++ = '{';
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& item : map) {
|
||||||
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
|
out = detail::write_range_entry<Char>(out, item.first);
|
||||||
|
*out++ = ':';
|
||||||
|
*out++ = ' ';
|
||||||
|
out = detail::write_range_entry<Char>(out, item.second);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
*out++ = '}';
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -374,46 +675,70 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
|||||||
template <typename Char, typename... T>
|
template <typename Char, typename... T>
|
||||||
using tuple_arg_join = tuple_join_view<Char, T...>;
|
using tuple_arg_join = tuple_join_view<Char, T...>;
|
||||||
|
|
||||||
|
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||||
|
// support in tuple_join. It is disabled by default because of issues with
|
||||||
|
// the dynamic width and precision.
|
||||||
|
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename Char, typename... T>
|
template <typename Char, typename... T>
|
||||||
struct formatter<tuple_join_view<Char, T...>, Char> {
|
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
|
auto format(const tuple_join_view<Char, T...>& value,
|
||||||
typename FormatContext::iterator {
|
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
return do_format(value, ctx,
|
||||||
|
std::integral_constant<size_t, sizeof...(T)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename FormatContext, size_t... N>
|
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||||
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
|
||||||
detail::index_sequence<N...>) ->
|
template <typename ParseContext>
|
||||||
typename FormatContext::iterator {
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
using std::get;
|
std::integral_constant<size_t, 0>)
|
||||||
return format_args(value, ctx, get<N>(value.tuple)...);
|
-> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParseContext, size_t N>
|
||||||
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
|
std::integral_constant<size_t, N>)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto end = ctx.begin();
|
||||||
|
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||||
|
if (N > 1) {
|
||||||
|
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||||
|
if (end != end1)
|
||||||
|
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
|
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||||
|
std::integral_constant<size_t, 0>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
|
||||||
// can be replaced with a constexpr branch in the variadic overload.
|
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, typename Arg, typename... Args>
|
template <typename FormatContext, size_t N>
|
||||||
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||||
const Arg& arg, const Args&... args) ->
|
std::integral_constant<size_t, N>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||||
auto out = base().format(arg, ctx);
|
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||||
if (sizeof...(Args) > 0) {
|
if (N > 1) {
|
||||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
return format_args(value, ctx, args...);
|
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#ifndef FMT_WCHAR_H_
|
#ifndef FMT_XCHAR_H_
|
||||||
#define FMT_WCHAR_H_
|
#define FMT_XCHAR_H_
|
||||||
|
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@ -217,11 +217,11 @@ inline void vprint(wstring_view fmt, wformat_args args) {
|
|||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||||
return vprint(f, wstring_view(fmt), make_wformat_args(args...));
|
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||||
return vprint(wstring_view(fmt), make_wformat_args(args...));
|
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,4 +233,4 @@ template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
|||||||
FMT_MODULE_EXPORT_END
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_WCHAR_H_
|
#endif // FMT_XCHAR_H_
|
||||||
|
113
src/fmt.cpp
113
src/fmt.cpp
@ -12,57 +12,118 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<typename T>
|
// DEPRECATED!
|
||||||
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
|
template <typename T = void> struct basic_data {
|
||||||
{
|
FMT_API static constexpr const char digits[100][2] = {
|
||||||
# ifdef FMT_FUZZ
|
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
|
||||||
if (precision > 100000)
|
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
|
||||||
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
|
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
|
||||||
# endif
|
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
|
||||||
// Suppress the warning about nonliteral format string.
|
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
|
||||||
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
|
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
|
||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
|
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
|
||||||
|
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
|
||||||
|
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
|
||||||
|
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
|
||||||
|
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
|
||||||
|
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
|
||||||
|
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
|
||||||
|
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
|
||||||
|
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
|
||||||
|
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
|
||||||
|
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
|
||||||
|
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
|
||||||
|
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
|
||||||
|
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
|
||||||
|
0};
|
||||||
|
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
|
||||||
|
0};
|
||||||
|
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
|
||||||
|
0x1000000u | ' '};
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef FMT_SHARED
|
||||||
|
// Required for -flto, -fivisibility=hidden and -shared to work
|
||||||
|
extern template struct basic_data<void>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cplusplus < 201703L
|
||||||
|
// DEPRECATED! These are here only for ABI compatiblity.
|
||||||
|
template <typename T> constexpr const char basic_data<T>::digits[][2];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::signs[];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
||||||
|
template <typename T>
|
||||||
|
constexpr const char basic_data<T>::right_padding_shifts[];
|
||||||
|
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
|
T value) {
|
||||||
|
#ifdef FMT_FUZZ
|
||||||
|
if (precision > 100000)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
|
#endif
|
||||||
|
// Suppress the warning about nonliteral format string.
|
||||||
|
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||||
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x) FMT_NOEXCEPT;
|
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||||
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
} // namespace detail
|
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer<char> &) = detail::format_float;
|
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&) = detail::format_float;
|
||||||
|
|
||||||
# ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
template FMT_API detail::locale_ref::locale_ref(const std::locale &loc);
|
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
|
||||||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||||
# endif
|
#endif
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result<char>;
|
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||||
|
-> thousands_sep_result<char>;
|
||||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<char>::append(const char *, const char *);
|
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
// DEPRECATED!
|
// DEPRECATED!
|
||||||
// There is no correspondent extern template in format.h because of
|
// There is no correspondent extern template in format.h because of
|
||||||
// incompatibility between clang and gcc (#2377).
|
// incompatibility between clang and gcc (#2377).
|
||||||
template FMT_API void detail::vformat_to(
|
template FMT_API void detail::vformat_to(
|
||||||
detail::buffer<char> &, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
detail::buffer<char>&, string_view,
|
||||||
|
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||||
|
|
||||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer<char> &);
|
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||||
template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
detail::buffer<char>&);
|
||||||
template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer<char> &);
|
template FMT_API int detail::snprintf_float(long double, int,
|
||||||
template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
template FMT_API int detail::format_float(double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result<wchar_t>;
|
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||||
|
-> thousands_sep_result<wchar_t>;
|
||||||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
|
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||||
|
const wchar_t*);
|
||||||
|
|
||||||
template struct detail::basic_data<void>;
|
template struct detail::basic_data<void>;
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
#endif // !SPDLOG_FMT_EXTERNAL
|
#endif // !SPDLOG_FMT_EXTERNAL
|
||||||
|
Loading…
Reference in New Issue
Block a user