Basics of formatting at compile-time based on compile-time API (#2019)

This commit is contained in:
Alexey Ochapov
2020-11-29 19:59:11 +03:00
committed by GitHub
parent 119f7dc3d6
commit dac753b81e
4 changed files with 137 additions and 34 deletions

View File

@@ -390,7 +390,7 @@ inline buffer_appender<T> reserve(buffer_appender<T> it, size_t n) {
return it;
}
template <typename Iterator> inline Iterator& reserve(Iterator& it, size_t) {
template <typename Iterator> constexpr Iterator& reserve(Iterator& it, size_t) {
return it;
}
@@ -414,7 +414,7 @@ inline std::back_insert_iterator<Container> base_iterator(
}
template <typename Iterator>
inline Iterator base_iterator(Iterator, Iterator it) {
constexpr Iterator base_iterator(Iterator, Iterator it) {
return it;
}
@@ -587,14 +587,17 @@ using needs_conversion = bool_constant<
template <typename OutChar, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
FMT_CONSTEXPR OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
while (begin != end) *it++ = *begin++;
return it;
}
template <typename OutChar, typename InputIt,
FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
inline OutChar* copy_str(InputIt begin, InputIt end, OutChar* out) {
FMT_CONSTEXPR20 OutChar* copy_str(InputIt begin, InputIt end, OutChar* out) {
if (is_constant_evaluated()) {
return copy_str<OutChar, InputIt, OutChar*>(begin, end, out);
}
return std::uninitialized_copy(begin, end, out);
}
@@ -951,17 +954,7 @@ FMT_EXTERN template struct basic_data<void>;
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
struct data : basic_data<> {};
#ifdef FMT_BUILTIN_CLZLL
// Returns the number of decimal digits in n. Leading zeros are not counted
// except for n == 0 in which case count_digits returns 1.
inline int count_digits(uint64_t n) {
// https://github.com/fmtlib/format-benchmark/blob/master/digits10
auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63);
return t - (n < data::zero_or_powers_of_10_64_new[t]);
}
#else
// Fallback version of count_digits used when __builtin_clz is not available.
inline int count_digits(uint64_t n) {
template <typename T> FMT_CONSTEXPR int count_digits_fallback(T n) {
int count = 1;
for (;;) {
// Integer division is slow so do it for a group of four digits instead
@@ -975,10 +968,25 @@ inline int count_digits(uint64_t n) {
count += 4;
}
}
#ifdef FMT_BUILTIN_CLZLL
// Returns the number of decimal digits in n. Leading zeros are not counted
// except for n == 0 in which case count_digits returns 1.
FMT_CONSTEXPR20 int count_digits(uint64_t n) {
if (is_constant_evaluated()) {
return count_digits_fallback(n);
}
// https://github.com/fmtlib/format-benchmark/blob/master/digits10
auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63);
return t - (n < data::zero_or_powers_of_10_64_new[t]);
}
#else
// Fallback version of count_digits used when __builtin_clz is not available.
FMT_CONSTEXPR int count_digits(uint64_t n) { return count_digits_fallback(n); }
#endif
#if FMT_USE_INT128
inline int count_digits(uint128_t n) {
FMT_CONSTEXPR int count_digits(uint128_t n) {
int count = 1;
for (;;) {
// Integer division is slow so do it for a group of four digits instead
@@ -995,7 +1003,7 @@ inline int count_digits(uint128_t n) {
#endif
// Counts the number of digits in n. BITS = log2(radix).
template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
template <unsigned BITS, typename UInt> FMT_CONSTEXPR int count_digits(UInt n) {
int num_digits = 0;
do {
++num_digits;
@@ -1015,7 +1023,10 @@ template <> int count_digits<4>(detail::fallback_uintptr n);
#ifdef FMT_BUILTIN_CLZ
// Optional version of count_digits for better performance on 32-bit platforms.
inline int count_digits(uint32_t n) {
FMT_CONSTEXPR20 int count_digits(uint32_t n) {
if (is_constant_evaluated()) {
return count_digits_fallback(n);
}
auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31);
return t - (n < data::zero_or_powers_of_10_32_new[t]);
}
@@ -1075,11 +1086,20 @@ template <typename Iterator> struct format_decimal_result {
// buffer of specified size. The caller must ensure that the buffer is large
// enough.
template <typename Char, typename UInt>
inline format_decimal_result<Char*> format_decimal(Char* out, UInt value,
int size) {
FMT_CONSTEXPR20 format_decimal_result<Char*> format_decimal(Char* out,
UInt value,
int size) {
FMT_ASSERT(size >= count_digits(value), "invalid digit count");
out += size;
Char* end = out;
if (is_constant_evaluated()) {
while (value >= 10) {
*--out = static_cast<Char>('0' + value % 10);
value /= 10;
}
*--out = static_cast<Char>('0' + value);
return {out, end};
}
while (value >= 100) {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
@@ -2048,7 +2068,7 @@ OutputIt write(OutputIt out, string_view value) {
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, basic_string_view<Char> value) {
FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view<Char> value) {
auto it = reserve(out, value.size());
it = copy_str<Char>(value.begin(), value.end(), it);
return base_iterator(out, it);
@@ -2058,7 +2078,7 @@ template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value &&
!std::is_same<T, Char>::value)>
OutputIt write(OutputIt out, T value) {
FMT_CONSTEXPR OutputIt write(OutputIt out, T value) {
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
bool negative = is_negative(value);
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
@@ -2077,19 +2097,19 @@ OutputIt write(OutputIt out, T value) {
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, bool value) {
constexpr OutputIt write(OutputIt out, bool value) {
return write<Char>(out, string_view(value ? "true" : "false"));
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, Char value) {
FMT_CONSTEXPR OutputIt write(OutputIt out, Char value) {
auto it = reserve(out, 1);
*it++ = value;
return base_iterator(out, it);
}
template <typename Char, typename OutputIt>
OutputIt write(OutputIt out, const Char* value) {
FMT_CONSTEXPR OutputIt write(OutputIt out, const Char* value) {
if (!value) {
FMT_THROW(format_error("string pointer is null"));
} else {