From 3f4cfa6c60f7e866eed109a05439c56ea3ce8980 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 30 Sep 2018 14:09:03 -0700 Subject: [PATCH] Implement UTF-8 string support --- include/fmt/format.h | 105 ++++++++++++++++++++----------------------- test/format-test.cc | 4 ++ 2 files changed, 52 insertions(+), 57 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index f37db274..17d2a3da 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -925,6 +925,28 @@ inline unsigned count_digits(uint64_t n) { // Counts the number of code points in a UTF-8 string. FMT_API size_t count_code_points(u8string_view s); +inline char8_t to_char8_t(char c) { return static_cast(c); } + +template +struct needs_conversion: std::integral_constant::value_type, char>::value && + std::is_same::value> {}; + +template +typename std::enable_if< + !needs_conversion::value, OutputIt>::type + copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::copy(begin, end, it); +} + +template +typename std::enable_if< + needs_conversion::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) # define FMT_ALWAYS_INLINE __attribute__((always_inline)) #else @@ -1074,19 +1096,20 @@ inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits, return end; } -template +template inline Iterator format_decimal( Iterator out, UInt value, unsigned num_digits, ThousandsSep sep) { typedef typename ThousandsSep::char_type char_type; // Buffer should be large enough to hold all digits (digits10 + 1) and null. char_type buffer[std::numeric_limits::digits10 + 2]; format_decimal(buffer, value, num_digits, sep); - return std::copy_n(buffer, num_digits, out); + return internal::copy_str(buffer, buffer + num_digits, out); } -template +template inline It format_decimal(It out, UInt value, unsigned num_digits) { - return format_decimal(out, value, num_digits, no_thousands_sep()); + return format_decimal(out, value, num_digits, no_thousands_sep()); } template @@ -1102,14 +1125,14 @@ inline Char *format_uint(Char *buffer, UInt value, unsigned num_digits, return end; } -template +template inline It format_uint(It out, UInt value, unsigned num_digits, bool upper = false) { // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1) // and null. char buffer[std::numeric_limits::digits / BASE_BITS + 2]; format_uint(buffer, value, num_digits, upper); - return std::copy_n(buffer, num_digits, out); + return internal::copy_str(buffer, buffer + num_digits, out); } #ifndef _WIN32 @@ -2300,28 +2323,6 @@ void handle_dynamic_spec( break; } } - -inline char8_t to_char8_t(char c) { return static_cast(c); } - -template -struct needs_conversion: std::integral_constant::value_type, char>::value && - std::is_same::value> {}; - -template -typename std::enable_if< - !needs_conversion::value, OutputIt>::type - copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::copy(begin, end, it); -} - -template -typename std::enable_if< - needs_conversion::value, OutputIt>::type - copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::transform(begin, end, it, to_char8_t); -} } // namespace internal /** The default argument formatter. */ @@ -2507,7 +2508,7 @@ class basic_writer { auto &&it = reserve((is_negative ? 1 : 0) + num_digits); if (is_negative) *it++ = static_cast('-'); - it = internal::format_decimal(it, abs_value, num_digits); + it = internal::format_decimal(it, abs_value, num_digits); } // The handle_int_type_spec handler that writes an integer. @@ -2553,7 +2554,7 @@ class basic_writer { template void operator()(It &&it) const { - it = internal::format_decimal(it, abs_value, num_digits); + it = internal::format_decimal(it, abs_value, num_digits); } }; @@ -2569,8 +2570,8 @@ class basic_writer { template void operator()(It &&it) const { - it = internal::format_uint<4>(it, self.abs_value, num_digits, - self.spec.type() != 'x'); + it = internal::format_uint<4, char_type>( + it, self.abs_value, num_digits, self.spec.type() != 'x'); } }; @@ -2591,7 +2592,7 @@ class basic_writer { template void operator()(It &&it) const { - it = internal::format_uint(it, abs_value, num_digits); + it = internal::format_uint(it, abs_value, num_digits); } }; @@ -2627,8 +2628,8 @@ class basic_writer { template void operator()(It &&it) const { basic_string_view s(&sep, SEP_SIZE); - it = format_decimal(it, abs_value, size, - internal::add_thousands_sep(s)); + it = internal::format_decimal( + it, abs_value, size, internal::add_thousands_sep(s)); } }; @@ -2670,7 +2671,7 @@ class basic_writer { struct double_writer { size_t n; char sign; - basic_memory_buffer &buffer; + internal::buffer &buffer; template void operator()(It &&it) { @@ -2678,7 +2679,7 @@ class basic_writer { *it++ = static_cast(sign); --n; } - it = std::copy_n(buffer.begin(), n, it); + it = internal::copy_str(buffer.begin(), buffer.end(), it); } }; @@ -2687,7 +2688,7 @@ class basic_writer { void write_double(T value, const format_specs &spec); template void write_double_sprintf(T value, const format_specs &spec, - internal::basic_buffer &buffer); + internal::buffer &buffer); template struct str_writer { @@ -2709,15 +2710,6 @@ class basic_writer { template void write_str(basic_string_view 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 - void append_float_length(char_type *&, T) {} - template friend class internal::arg_formatter_base; @@ -2907,7 +2899,7 @@ void basic_writer::write_double(T value, const format_specs &spec) { if (internal::fputil::isinfinity(value)) return write_inf_or_nan(handler.upper ? "INF" : "inf"); - basic_memory_buffer buffer; + memory_buffer buffer; char type = static_cast(spec.type()); if (internal::const_check( internal::use_grisu() && sizeof(T) <= sizeof(double)) && @@ -2946,15 +2938,14 @@ void basic_writer::write_double(T value, const format_specs &spec) { template template void basic_writer::write_double_sprintf( - T value, const format_specs &spec, - internal::basic_buffer &buffer) { + T value, const format_specs &spec, internal::buffer &buffer) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buffer.capacity() != 0, "empty buffer"); // Build format string. enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - char_type format[MAX_FORMAT_SIZE]; - char_type *format_ptr = format; + char format[MAX_FORMAT_SIZE]; + char *format_ptr = format; *format_ptr++ = '%'; if (spec.flag(HASH_FLAG)) *format_ptr++ = '#'; @@ -2962,17 +2953,17 @@ void basic_writer::write_double_sprintf( *format_ptr++ = '.'; *format_ptr++ = '*'; } - - append_float_length(format_ptr, value); + if (std::is_same::value) + *format_ptr++ = 'L'; *format_ptr++ = spec.type(); *format_ptr = '\0'; // Format using snprintf. - char_type *start = FMT_NULL; + char *start = FMT_NULL; for (;;) { std::size_t buffer_size = buffer.capacity(); start = &buffer[0]; - int result = internal::char_traits::format_float( + int result = internal::char_traits::format_float( start, buffer_size, format, spec.precision(), value); if (result >= 0) { unsigned n = internal::to_unsigned(result); @@ -3142,7 +3133,7 @@ inline void format_decimal(char *&buffer, T value) { return; } unsigned num_digits = internal::count_digits(abs_value); - internal::format_decimal(buffer, abs_value, num_digits); + internal::format_decimal(buffer, abs_value, num_digits); buffer += num_digits; } diff --git a/test/format-test.cc b/test/format-test.cc index 3ceeebbb..18f8ad12 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2430,3 +2430,7 @@ TEST(FormatTest, U8StringViewLiteral) { EXPECT_EQ(data[1], 'b'); } #endif + +TEST(FormatTest, FormatU8String) { + EXPECT_EQ(format(fmt::u8string_view("{}"), 42), fmt::u8string_view("42")); +}