mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 02:37:36 +02:00
Improve handling of separators
This commit is contained in:
@ -886,34 +886,31 @@ template <> inline wchar_t decimal_point(locale_ref loc) {
|
|||||||
return decimal_point_impl<wchar_t>(loc);
|
return decimal_point_impl<wchar_t>(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats a decimal unsigned integer value writing into buffer.
|
template <typename Char> void copy2(Char* dst, const char* src) {
|
||||||
// add_thousands_sep is called after writing each char to add a thousands
|
*dst++ = static_cast<Char>(*src++);
|
||||||
// separator if necessary.
|
*dst = static_cast<Char>(*src);
|
||||||
template <typename UInt, typename Char, typename F>
|
}
|
||||||
inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
|
inline void copy2(char* dst, const char* src) { memcpy(dst, src, 2); }
|
||||||
F add_thousands_sep) {
|
|
||||||
|
// Formats a decimal unsigned integer value writing into out.
|
||||||
|
template <typename UInt, typename Char>
|
||||||
|
inline Char* format_decimal(Char* out, UInt value, int num_digits) {
|
||||||
FMT_ASSERT(num_digits >= 0, "invalid digit count");
|
FMT_ASSERT(num_digits >= 0, "invalid digit count");
|
||||||
buffer += num_digits;
|
out += num_digits;
|
||||||
Char* end = buffer;
|
Char* end = out;
|
||||||
while (value >= 100) {
|
while (value >= 100) {
|
||||||
// Integer division is slow so do it for a group of two digits instead
|
// 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
|
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||||
auto index = static_cast<unsigned>((value % 100) * 2);
|
out -= 2;
|
||||||
|
copy2(out, data::digits + static_cast<unsigned>((value % 100) * 2));
|
||||||
value /= 100;
|
value /= 100;
|
||||||
*--buffer = static_cast<Char>(data::digits[index + 1]);
|
|
||||||
add_thousands_sep(buffer);
|
|
||||||
*--buffer = static_cast<Char>(data::digits[index]);
|
|
||||||
add_thousands_sep(buffer);
|
|
||||||
}
|
}
|
||||||
if (value < 10) {
|
if (value < 10) {
|
||||||
*--buffer = static_cast<Char>('0' + value);
|
*--out = static_cast<Char>('0' + value);
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
auto index = static_cast<unsigned>(value * 2);
|
copy2(out - 2, data::digits + static_cast<unsigned>(value * 2));
|
||||||
*--buffer = static_cast<Char>(data::digits[index + 1]);
|
|
||||||
add_thousands_sep(buffer);
|
|
||||||
*--buffer = static_cast<Char>(data::digits[index]);
|
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,22 +920,15 @@ template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
|
|||||||
template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
|
template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
|
||||||
template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
|
template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
|
||||||
|
|
||||||
template <typename Char, typename UInt, typename Iterator, typename F>
|
template <typename Char, typename UInt, typename Iterator>
|
||||||
inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
|
inline Iterator format_decimal(Iterator out, UInt value, int num_digits) {
|
||||||
F add_thousands_sep) {
|
|
||||||
FMT_ASSERT(num_digits >= 0, "invalid digit count");
|
|
||||||
// Buffer should be large enough to hold all digits (<= digits10 + 1).
|
// Buffer should be large enough to hold all digits (<= digits10 + 1).
|
||||||
enum { max_size = digits10<UInt>() + 1 };
|
enum { max_size = digits10<UInt>() + 1 };
|
||||||
Char buffer[2 * max_size];
|
Char buffer[2 * max_size];
|
||||||
auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
|
auto end = format_decimal(buffer, value, num_digits);
|
||||||
return detail::copy_str<Char>(buffer, end, out);
|
return detail::copy_str<Char>(buffer, end, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename It, typename UInt>
|
|
||||||
inline It format_decimal(It out, UInt value, int num_digits) {
|
|
||||||
return format_decimal<Char>(out, value, num_digits, [](Char*) {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <unsigned BASE_BITS, typename Char, typename UInt>
|
template <unsigned BASE_BITS, typename Char, typename UInt>
|
||||||
inline Char* format_uint(Char* buffer, UInt value, int num_digits,
|
inline Char* format_uint(Char* buffer, UInt value, int num_digits,
|
||||||
bool upper = false) {
|
bool upper = false) {
|
||||||
@ -1412,6 +1402,16 @@ inline OutputIt write_padded(OutputIt out,
|
|||||||
return write_padded<align>(out, specs, size, size, f);
|
return write_padded<align>(out, specs, size, size, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
OutputIt write_bytes(OutputIt out, string_view bytes,
|
||||||
|
const basic_format_specs<Char>& specs) {
|
||||||
|
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
||||||
|
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
|
||||||
|
const char* data = bytes.data();
|
||||||
|
return copy_str<Char>(data, data + bytes.size(), it);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Data for write_int that doesn't depend on output iterator type. It is used to
|
// Data for write_int that doesn't depend on output iterator type. It is used to
|
||||||
// avoid template code bloat.
|
// avoid template code bloat.
|
||||||
template <typename Char> struct write_int_data {
|
template <typename Char> struct write_int_data {
|
||||||
@ -1531,52 +1531,43 @@ template <typename OutputIt, typename Char, typename UInt> struct int_writer {
|
|||||||
|
|
||||||
enum { sep_size = 1 };
|
enum { sep_size = 1 };
|
||||||
|
|
||||||
struct num_writer {
|
|
||||||
UInt abs_value;
|
|
||||||
int size;
|
|
||||||
const std::string& groups;
|
|
||||||
Char sep;
|
|
||||||
|
|
||||||
template <typename It> It operator()(It it) const {
|
|
||||||
basic_string_view<Char> s(&sep, sep_size);
|
|
||||||
// Index of a decimal digit with the least significant digit having
|
|
||||||
// index 0.
|
|
||||||
int digit_index = 0;
|
|
||||||
std::string::const_iterator group = groups.cbegin();
|
|
||||||
return format_decimal<Char>(
|
|
||||||
it, abs_value, size, [this, s, &group, &digit_index](Char*& buffer) {
|
|
||||||
if (*group <= 0 || ++digit_index % *group != 0 ||
|
|
||||||
*group == max_value<char>())
|
|
||||||
return;
|
|
||||||
if (group + 1 != groups.cend()) {
|
|
||||||
digit_index = 0;
|
|
||||||
++group;
|
|
||||||
}
|
|
||||||
buffer -= s.size();
|
|
||||||
std::uninitialized_copy(s.data(), s.data() + s.size(),
|
|
||||||
make_checked(buffer, s.size()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void on_num() {
|
void on_num() {
|
||||||
std::string groups = grouping<Char>(locale);
|
std::string groups = grouping<Char>(locale);
|
||||||
if (groups.empty()) return on_dec();
|
if (groups.empty()) return on_dec();
|
||||||
auto sep = thousands_sep<Char>(locale);
|
auto sep = thousands_sep<Char>(locale);
|
||||||
if (!sep) return on_dec();
|
if (!sep) return on_dec();
|
||||||
int num_digits = count_digits(abs_value);
|
int num_digits = count_digits(abs_value);
|
||||||
int size = num_digits;
|
int size = num_digits, n = num_digits;
|
||||||
std::string::const_iterator group = groups.cbegin();
|
std::string::const_iterator group = groups.cbegin();
|
||||||
while (group != groups.cend() && num_digits > *group && *group > 0 &&
|
while (group != groups.cend() && num_digits > *group && *group > 0 &&
|
||||||
*group != max_value<char>()) {
|
*group != max_value<char>()) {
|
||||||
size += sep_size;
|
size += sep_size;
|
||||||
num_digits -= *group;
|
n -= *group;
|
||||||
++group;
|
++group;
|
||||||
}
|
}
|
||||||
if (group == groups.cend())
|
if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
|
||||||
size += sep_size * ((num_digits - 1) / groups.back());
|
char digits[40];
|
||||||
out = write_int(out, size, get_prefix(), specs,
|
format_decimal<Char>(digits, abs_value, num_digits);
|
||||||
num_writer{abs_value, size, groups, sep});
|
memory_buffer buffer;
|
||||||
|
buffer.resize(size);
|
||||||
|
basic_string_view<Char> s(&sep, sep_size);
|
||||||
|
// Index of a decimal digit with the least significant digit having index 0.
|
||||||
|
int digit_index = 0;
|
||||||
|
group = groups.cbegin();
|
||||||
|
auto p = buffer.data() + size;
|
||||||
|
for (int i = num_digits - 1; i >= 0; --i) {
|
||||||
|
*--p = digits[i];
|
||||||
|
if (*group <= 0 || ++digit_index % *group != 0 ||
|
||||||
|
*group == max_value<char>())
|
||||||
|
continue;
|
||||||
|
if (group + 1 != groups.cend()) {
|
||||||
|
digit_index = 0;
|
||||||
|
++group;
|
||||||
|
}
|
||||||
|
p -= s.size();
|
||||||
|
std::uninitialized_copy(s.data(), s.data() + s.size(), p);
|
||||||
|
}
|
||||||
|
write_bytes(out, {buffer.data(), buffer.size()}, specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_chr() { *out++ = static_cast<Char>(abs_value); }
|
void on_chr() { *out++ = static_cast<Char>(abs_value); }
|
||||||
@ -1586,16 +1577,6 @@ template <typename OutputIt, typename Char, typename UInt> struct int_writer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt write_bytes(OutputIt out, string_view bytes,
|
|
||||||
const basic_format_specs<Char>& specs) {
|
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
|
||||||
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
|
|
||||||
const char* data = bytes.data();
|
|
||||||
return copy_str<Char>(data, data + bytes.size(), it);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
OutputIt write_nonfinite(OutputIt out, bool isinf,
|
OutputIt write_nonfinite(OutputIt out, bool isinf,
|
||||||
const basic_format_specs<Char>& specs,
|
const basic_format_specs<Char>& specs,
|
||||||
@ -3175,8 +3156,7 @@ struct format_handler : detail::error_handler {
|
|||||||
basic_format_arg<Context> arg;
|
basic_format_arg<Context> arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char> bool equal2(const Char* lhs, const char* rhs) {
|
||||||
bool equal2(const Char* lhs, const char* rhs) {
|
|
||||||
return lhs[0] == rhs[0] && lhs[1] == rhs[1];
|
return lhs[0] == rhs[0] && lhs[1] == rhs[1];
|
||||||
}
|
}
|
||||||
inline bool equal2(const char* lhs, const char* rhs) {
|
inline bool equal2(const char* lhs, const char* rhs) {
|
||||||
|
Reference in New Issue
Block a user