mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 10:47:35 +02:00
Implement UTF-8 string support
This commit is contained in:
@ -925,6 +925,28 @@ inline unsigned count_digits(uint64_t n) {
|
|||||||
// Counts the number of code points in a UTF-8 string.
|
// Counts the number of code points in a UTF-8 string.
|
||||||
FMT_API size_t count_code_points(u8string_view s);
|
FMT_API size_t count_code_points(u8string_view s);
|
||||||
|
|
||||||
|
inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
|
||||||
|
|
||||||
|
template <typename InputIt, typename OutChar>
|
||||||
|
struct needs_conversion: std::integral_constant<bool,
|
||||||
|
std::is_same<
|
||||||
|
typename std::iterator_traits<InputIt>::value_type, char>::value &&
|
||||||
|
std::is_same<OutChar, char8_t>::value> {};
|
||||||
|
|
||||||
|
template <typename OutChar, typename InputIt, typename OutputIt>
|
||||||
|
typename std::enable_if<
|
||||||
|
!needs_conversion<InputIt, OutChar>::value, OutputIt>::type
|
||||||
|
copy_str(InputIt begin, InputIt end, OutputIt it) {
|
||||||
|
return std::copy(begin, end, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutChar, typename InputIt, typename OutputIt>
|
||||||
|
typename std::enable_if<
|
||||||
|
needs_conversion<InputIt, OutChar>::value, OutputIt>::type
|
||||||
|
copy_str(InputIt begin, InputIt end, OutputIt it) {
|
||||||
|
return std::transform(begin, end, it, to_char8_t);
|
||||||
|
}
|
||||||
|
|
||||||
#if FMT_HAS_CPP_ATTRIBUTE(always_inline)
|
#if FMT_HAS_CPP_ATTRIBUTE(always_inline)
|
||||||
# define FMT_ALWAYS_INLINE __attribute__((always_inline))
|
# define FMT_ALWAYS_INLINE __attribute__((always_inline))
|
||||||
#else
|
#else
|
||||||
@ -1074,19 +1096,20 @@ inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits,
|
|||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename UInt, typename Iterator, typename ThousandsSep>
|
template <typename OutChar, typename UInt, typename Iterator,
|
||||||
|
typename ThousandsSep>
|
||||||
inline Iterator format_decimal(
|
inline Iterator format_decimal(
|
||||||
Iterator out, UInt value, unsigned num_digits, ThousandsSep sep) {
|
Iterator out, UInt value, unsigned num_digits, ThousandsSep sep) {
|
||||||
typedef typename ThousandsSep::char_type char_type;
|
typedef typename ThousandsSep::char_type char_type;
|
||||||
// Buffer should be large enough to hold all digits (digits10 + 1) and null.
|
// Buffer should be large enough to hold all digits (digits10 + 1) and null.
|
||||||
char_type buffer[std::numeric_limits<UInt>::digits10 + 2];
|
char_type buffer[std::numeric_limits<UInt>::digits10 + 2];
|
||||||
format_decimal(buffer, value, num_digits, sep);
|
format_decimal(buffer, value, num_digits, sep);
|
||||||
return std::copy_n(buffer, num_digits, out);
|
return internal::copy_str<OutChar>(buffer, buffer + num_digits, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename It, typename UInt>
|
template <typename OutChar, typename It, typename UInt>
|
||||||
inline It format_decimal(It out, UInt value, unsigned num_digits) {
|
inline It format_decimal(It out, UInt value, unsigned num_digits) {
|
||||||
return format_decimal(out, value, num_digits, no_thousands_sep());
|
return format_decimal<OutChar>(out, value, num_digits, no_thousands_sep());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned BASE_BITS, typename Char, typename UInt>
|
template <unsigned BASE_BITS, typename Char, typename UInt>
|
||||||
@ -1102,14 +1125,14 @@ inline Char *format_uint(Char *buffer, UInt value, unsigned num_digits,
|
|||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned BASE_BITS, typename It, typename UInt>
|
template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
|
||||||
inline It format_uint(It out, UInt value, unsigned num_digits,
|
inline It format_uint(It out, UInt value, unsigned num_digits,
|
||||||
bool upper = false) {
|
bool upper = false) {
|
||||||
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1)
|
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1)
|
||||||
// and null.
|
// and null.
|
||||||
char buffer[std::numeric_limits<UInt>::digits / BASE_BITS + 2];
|
char buffer[std::numeric_limits<UInt>::digits / BASE_BITS + 2];
|
||||||
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
|
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
|
||||||
return std::copy_n(buffer, num_digits, out);
|
return internal::copy_str<Char>(buffer, buffer + num_digits, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@ -2300,28 +2323,6 @@ void handle_dynamic_spec(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
|
|
||||||
|
|
||||||
template <typename InputIt, typename OutChar>
|
|
||||||
struct needs_conversion: std::integral_constant<bool,
|
|
||||||
std::is_same<
|
|
||||||
typename std::iterator_traits<InputIt>::value_type, char>::value &&
|
|
||||||
std::is_same<OutChar, char8_t>::value> {};
|
|
||||||
|
|
||||||
template <typename OutChar, typename InputIt, typename OutputIt>
|
|
||||||
typename std::enable_if<
|
|
||||||
!needs_conversion<InputIt, OutChar>::value, OutputIt>::type
|
|
||||||
copy_str(InputIt begin, InputIt end, OutputIt it) {
|
|
||||||
return std::copy(begin, end, it);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutChar, typename InputIt, typename OutputIt>
|
|
||||||
typename std::enable_if<
|
|
||||||
needs_conversion<InputIt, OutChar>::value, OutputIt>::type
|
|
||||||
copy_str(InputIt begin, InputIt end, OutputIt it) {
|
|
||||||
return std::transform(begin, end, it, to_char8_t);
|
|
||||||
}
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
/** The default argument formatter. */
|
/** The default argument formatter. */
|
||||||
@ -2507,7 +2508,7 @@ class basic_writer {
|
|||||||
auto &&it = reserve((is_negative ? 1 : 0) + num_digits);
|
auto &&it = reserve((is_negative ? 1 : 0) + num_digits);
|
||||||
if (is_negative)
|
if (is_negative)
|
||||||
*it++ = static_cast<char_type>('-');
|
*it++ = static_cast<char_type>('-');
|
||||||
it = internal::format_decimal(it, abs_value, num_digits);
|
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handle_int_type_spec handler that writes an integer.
|
// The handle_int_type_spec handler that writes an integer.
|
||||||
@ -2553,7 +2554,7 @@ class basic_writer {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
void operator()(It &&it) const {
|
void operator()(It &&it) const {
|
||||||
it = internal::format_decimal(it, abs_value, num_digits);
|
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2569,8 +2570,8 @@ class basic_writer {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
void operator()(It &&it) const {
|
void operator()(It &&it) const {
|
||||||
it = internal::format_uint<4>(it, self.abs_value, num_digits,
|
it = internal::format_uint<4, char_type>(
|
||||||
self.spec.type() != 'x');
|
it, self.abs_value, num_digits, self.spec.type() != 'x');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2591,7 +2592,7 @@ class basic_writer {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
void operator()(It &&it) const {
|
void operator()(It &&it) const {
|
||||||
it = internal::format_uint<BITS>(it, abs_value, num_digits);
|
it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2627,8 +2628,8 @@ class basic_writer {
|
|||||||
template <typename It>
|
template <typename It>
|
||||||
void operator()(It &&it) const {
|
void operator()(It &&it) const {
|
||||||
basic_string_view<char_type> s(&sep, SEP_SIZE);
|
basic_string_view<char_type> s(&sep, SEP_SIZE);
|
||||||
it = format_decimal(it, abs_value, size,
|
it = internal::format_decimal<char_type>(
|
||||||
internal::add_thousands_sep<char_type>(s));
|
it, abs_value, size, internal::add_thousands_sep<char_type>(s));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2670,7 +2671,7 @@ class basic_writer {
|
|||||||
struct double_writer {
|
struct double_writer {
|
||||||
size_t n;
|
size_t n;
|
||||||
char sign;
|
char sign;
|
||||||
basic_memory_buffer<char_type> &buffer;
|
internal::buffer &buffer;
|
||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
void operator()(It &&it) {
|
void operator()(It &&it) {
|
||||||
@ -2678,7 +2679,7 @@ class basic_writer {
|
|||||||
*it++ = static_cast<char_type>(sign);
|
*it++ = static_cast<char_type>(sign);
|
||||||
--n;
|
--n;
|
||||||
}
|
}
|
||||||
it = std::copy_n(buffer.begin(), n, it);
|
it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2687,7 +2688,7 @@ class basic_writer {
|
|||||||
void write_double(T value, const format_specs &spec);
|
void write_double(T value, const format_specs &spec);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_double_sprintf(T value, const format_specs &spec,
|
void write_double_sprintf(T value, const format_specs &spec,
|
||||||
internal::basic_buffer<char_type> &buffer);
|
internal::buffer &buffer);
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct str_writer {
|
struct str_writer {
|
||||||
@ -2709,15 +2710,6 @@ class basic_writer {
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write_str(basic_string_view<Char> str, const format_specs &spec);
|
void write_str(basic_string_view<Char> str, const format_specs &spec);
|
||||||
|
|
||||||
// Appends floating-point length specifier to the format string.
|
|
||||||
// The second argument is only used for overload resolution.
|
|
||||||
void append_float_length(char_type *&format_ptr, long double) {
|
|
||||||
*format_ptr++ = 'L';
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void append_float_length(char_type *&, T) {}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
friend class internal::arg_formatter_base;
|
friend class internal::arg_formatter_base;
|
||||||
|
|
||||||
@ -2907,7 +2899,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
|||||||
if (internal::fputil::isinfinity(value))
|
if (internal::fputil::isinfinity(value))
|
||||||
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
||||||
|
|
||||||
basic_memory_buffer<char_type> buffer;
|
memory_buffer buffer;
|
||||||
char type = static_cast<char>(spec.type());
|
char type = static_cast<char>(spec.type());
|
||||||
if (internal::const_check(
|
if (internal::const_check(
|
||||||
internal::use_grisu() && sizeof(T) <= sizeof(double)) &&
|
internal::use_grisu() && sizeof(T) <= sizeof(double)) &&
|
||||||
@ -2946,15 +2938,14 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
|||||||
template <typename Range>
|
template <typename Range>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void basic_writer<Range>::write_double_sprintf(
|
void basic_writer<Range>::write_double_sprintf(
|
||||||
T value, const format_specs &spec,
|
T value, const format_specs &spec, internal::buffer &buffer) {
|
||||||
internal::basic_buffer<char_type> &buffer) {
|
|
||||||
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
FMT_ASSERT(buffer.capacity() != 0, "empty buffer");
|
FMT_ASSERT(buffer.capacity() != 0, "empty buffer");
|
||||||
|
|
||||||
// Build format string.
|
// Build format string.
|
||||||
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||||
char_type format[MAX_FORMAT_SIZE];
|
char format[MAX_FORMAT_SIZE];
|
||||||
char_type *format_ptr = format;
|
char *format_ptr = format;
|
||||||
*format_ptr++ = '%';
|
*format_ptr++ = '%';
|
||||||
if (spec.flag(HASH_FLAG))
|
if (spec.flag(HASH_FLAG))
|
||||||
*format_ptr++ = '#';
|
*format_ptr++ = '#';
|
||||||
@ -2962,17 +2953,17 @@ void basic_writer<Range>::write_double_sprintf(
|
|||||||
*format_ptr++ = '.';
|
*format_ptr++ = '.';
|
||||||
*format_ptr++ = '*';
|
*format_ptr++ = '*';
|
||||||
}
|
}
|
||||||
|
if (std::is_same<T, long double>::value)
|
||||||
append_float_length(format_ptr, value);
|
*format_ptr++ = 'L';
|
||||||
*format_ptr++ = spec.type();
|
*format_ptr++ = spec.type();
|
||||||
*format_ptr = '\0';
|
*format_ptr = '\0';
|
||||||
|
|
||||||
// Format using snprintf.
|
// Format using snprintf.
|
||||||
char_type *start = FMT_NULL;
|
char *start = FMT_NULL;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
std::size_t buffer_size = buffer.capacity();
|
std::size_t buffer_size = buffer.capacity();
|
||||||
start = &buffer[0];
|
start = &buffer[0];
|
||||||
int result = internal::char_traits<char_type>::format_float(
|
int result = internal::char_traits<char>::format_float(
|
||||||
start, buffer_size, format, spec.precision(), value);
|
start, buffer_size, format, spec.precision(), value);
|
||||||
if (result >= 0) {
|
if (result >= 0) {
|
||||||
unsigned n = internal::to_unsigned(result);
|
unsigned n = internal::to_unsigned(result);
|
||||||
@ -3142,7 +3133,7 @@ inline void format_decimal(char *&buffer, T value) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsigned num_digits = internal::count_digits(abs_value);
|
unsigned num_digits = internal::count_digits(abs_value);
|
||||||
internal::format_decimal(buffer, abs_value, num_digits);
|
internal::format_decimal<char>(buffer, abs_value, num_digits);
|
||||||
buffer += num_digits;
|
buffer += num_digits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2430,3 +2430,7 @@ TEST(FormatTest, U8StringViewLiteral) {
|
|||||||
EXPECT_EQ(data[1], 'b');
|
EXPECT_EQ(data[1], 'b');
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST(FormatTest, FormatU8String) {
|
||||||
|
EXPECT_EQ(format(fmt::u8string_view("{}"), 42), fmt::u8string_view("42"));
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user