[libc-commits] [libc] 509679c - [libc] Add support for writing to wchar_t in printf_core/ converter functions. (#230246)

via libc-commits libc-commits at lists.llvm.org
Thu Oct 15 08:03:07 PDT 2026


Author: Alex Strelnikov
Date: 2026-10-15T15:02:57Z
New Revision: 509679cf9c9db718d5b476a93ccffbd27a3ab980

URL: https://github.com/llvm/llvm-project/commit/509679cf9c9db718d5b476a93ccffbd27a3ab980
DIFF: https://github.com/llvm/llvm-project/commit/509679cf9c9db718d5b476a93ccffbd27a3ab980.diff

LOG: [libc] Add support for writing to wchar_t in printf_core/ converter functions. (#230246)

This is part of the ongoing work toward implementing wide-character
printf functions.

* A `CharT` template type parameter is added where appropriate to the
converter functions.
* The converter_test cases are extended to `TYPED_TEST`s covering both
`char` and `wchar_t`.
* Some new test cases are added for character and string conversion.
* As a part of this change `BasicFormatSection<T>` is renamed back to
simply `FormatSection<T>`. (It was renamed in #222988 with
`FormatSection` becoming an alias mainly to keep from changing a lot of
lines in converter_test twice.)

Assisted-by: Automated tooling, human reviewed.

Added: 
    

Modified: 
    libc/fuzzing/stdio/printf_parser_fuzz.cpp
    libc/src/__support/printf_core/CMakeLists.txt
    libc/src/__support/printf_core/char_converter.h
    libc/src/__support/printf_core/converter.h
    libc/src/__support/printf_core/core_structs.h
    libc/src/__support/printf_core/fixed_converter.h
    libc/src/__support/printf_core/float_dec_converter.h
    libc/src/__support/printf_core/float_dec_converter_limited.h
    libc/src/__support/printf_core/float_hex_converter.h
    libc/src/__support/printf_core/float_inf_nan_converter.h
    libc/src/__support/printf_core/int_converter.h
    libc/src/__support/printf_core/parser.h
    libc/src/__support/printf_core/printf_main.h
    libc/src/__support/printf_core/ptr_converter.h
    libc/src/__support/printf_core/strerror_converter.h
    libc/src/__support/printf_core/string_converter.h
    libc/src/__support/printf_core/write_int_converter.h
    libc/src/stdlib/str_from_util.h
    libc/test/UnitTest/PrintfMatcher.cpp
    libc/test/UnitTest/PrintfMatcher.h
    libc/test/src/__support/printf_core/converter_test.cpp
    libc/test/src/__support/printf_core/parser_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/fuzzing/stdio/printf_parser_fuzz.cpp b/libc/fuzzing/stdio/printf_parser_fuzz.cpp
index f85f2f949872be..e352f88e93bd49 100644
--- a/libc/fuzzing/stdio/printf_parser_fuzz.cpp
+++ b/libc/fuzzing/stdio/printf_parser_fuzz.cpp
@@ -46,7 +46,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 
   int section_percent_count = 0;
 
-  for (printf_core::FormatSection cur_section = parser.get_next_section();
+  for (printf_core::FormatSection<char> cur_section = parser.get_next_section();
        !cur_section.raw_string.empty();
        cur_section = parser.get_next_section()) {
     if (cur_section.has_conv) {

diff  --git a/libc/src/__support/printf_core/CMakeLists.txt b/libc/src/__support/printf_core/CMakeLists.txt
index 03b680010b00f0..d059d8881c4141 100644
--- a/libc/src/__support/printf_core/CMakeLists.txt
+++ b/libc/src/__support/printf_core/CMakeLists.txt
@@ -54,6 +54,7 @@ endif()
 if(LIBC_CONF_PRINTF_DISABLE_WIDE)
   set(wchar_deps "")
   set(parser_wchar_deps "")
+  set(converter_wchar_deps "")
 else()
   set(wchar_deps
     libc.hdr.types.wchar_t
@@ -68,6 +69,9 @@ else()
   set(parser_wchar_deps
     libc.hdr.types.wint_t
   )
+  set(converter_wchar_deps
+    libc.src.__support.wctype_utils
+  )
 endif()
 
 add_header_library(
@@ -152,11 +156,14 @@ add_header_library(
     libc.include.inttypes
     libc.hdr.limits_macros
     libc.src.string.string_utils
-    libc.src.__support.big_int
-    libc.src.__support.common
     libc.src.__support.CPP.limits
     libc.src.__support.CPP.span
     libc.src.__support.CPP.string_view
+    libc.src.__support.CPP.type_traits
+    libc.src.__support.StringUtil.error_to_string
+    libc.src.__support.big_int
+    libc.src.__support.common
+    libc.src.__support.ctype_utils
     libc.src.__support.float_to_string
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
@@ -164,9 +171,9 @@ add_header_library(
     libc.src.__support.integer_to_string
     libc.src.__support.libc_assert
     libc.src.__support.uint128
-    libc.src.__support.StringUtil.error_to_string
     libc.src.string.memory_utils.inline_memcpy
     ${wchar_deps}
+    ${converter_wchar_deps}
 )
 
 add_object_library(

diff  --git a/libc/src/__support/printf_core/char_converter.h b/libc/src/__support/printf_core/char_converter.h
index 7bad28d18d1bee..c78eec54a0affd 100644
--- a/libc/src/__support/printf_core/char_converter.h
+++ b/libc/src/__support/printf_core/char_converter.h
@@ -18,6 +18,7 @@
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
 #include "hdr/limits_macros.h"
+#include "src/__support/CPP/type_traits.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/printf_core/converter_utils.h"
 #include "src/__support/printf_core/core_structs.h"
@@ -27,9 +28,8 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 template <OverflowMode mode>
-LIBC_INLINE int convert_char(Writer<mode> *writer,
-                             const FormatSection &to_conv) {
-
+LIBC_INLINE int write_character_as_char(Writer<mode, char> *writer,
+                                        const FormatSection<char> &to_conv) {
   char buffer[MB_LEN_MAX];
   size_t write_size = 0;
 
@@ -82,6 +82,65 @@ LIBC_INLINE int convert_char(Writer<mode> *writer,
   return WRITE_OK;
 }
 
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+
+template <OverflowMode mode>
+LIBC_INLINE int
+write_character_as_wchar(Writer<mode, wchar_t> *writer,
+                         const FormatSection<wchar_t> &to_conv) {
+  wchar_t write_char;
+
+  if (to_conv.length_modifier == LengthModifier::l) {
+    wint_t wi = static_cast<wint_t>(to_conv.conv_val_raw);
+    if (wi == WEOF)
+      return ILLEGAL_WIDE_CHAR;
+
+    write_char = static_cast<wchar_t>(wi);
+  } else {
+    int c = static_cast<int>(to_conv.conv_val_raw);
+    if (c < 0 || c > 127)
+      return MB_CONVERSION_ERROR;
+
+    write_char = static_cast<wchar_t>(c);
+  }
+
+  size_t padding_spaces = to_conv.min_width > 1 ? to_conv.min_width - 1 : 0;
+
+  // If the padding is on the left side, write the spaces first.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(L' ', padding_spaces));
+  }
+
+  RET_IF_RESULT_NEGATIVE(writer->write(write_char));
+
+  // If the padding is on the right side, write the spaces last.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) != 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(L' ', padding_spaces));
+  }
+
+  return WRITE_OK;
+}
+
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_character(Writer<mode, CharT> *writer,
+                                  const FormatSection<CharT> &to_conv) {
+#if defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+  static_assert(cpp::is_same_v<CharT, char>, "wchar_t not supported.");
+  return write_character_as_char(writer, to_conv);
+#else  // LIBC_COPT_PRINTF_DISABLE_WIDE
+  if constexpr (cpp::is_same_v<CharT, char>) {
+    return write_character_as_char(writer, to_conv);
+  } else {
+    static_assert(cpp::is_same_v<CharT, wchar_t>);
+    return write_character_as_wchar(writer, to_conv);
+  }
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+}
+
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 

diff  --git a/libc/src/__support/printf_core/converter.h b/libc/src/__support/printf_core/converter.h
index 05ef6829b3a5eb..a37966a22bd46f 100644
--- a/libc/src/__support/printf_core/converter.h
+++ b/libc/src/__support/printf_core/converter.h
@@ -29,22 +29,22 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
-LIBC_PRINTF_MODULE((template <OverflowMode mode>
-                    int convert_float(Writer<mode> *writer,
-                                      const FormatSection &to_conv)),
+LIBC_PRINTF_MODULE((template <OverflowMode mode, typename CharT>
+                    int convert_float(Writer<mode, CharT> *writer,
+                                      const FormatSection<CharT> &to_conv)),
                    {
                      switch (to_conv.conv_name) {
-                     case 'f':
-                     case 'F':
+                     case CharT{'f'}:
+                     case CharT{'F'}:
                        return convert_float_decimal(writer, to_conv);
-                     case 'e':
-                     case 'E':
+                     case CharT{'e'}:
+                     case CharT{'E'}:
                        return convert_float_dec_exp(writer, to_conv);
-                     case 'a':
-                     case 'A':
+                     case CharT{'a'}:
+                     case CharT{'A'}:
                        return convert_float_hex_exp(writer, to_conv);
-                     case 'g':
-                     case 'G':
+                     case CharT{'g'}:
+                     case CharT{'G'}:
                        return convert_float_dec_auto(writer, to_conv);
                      }
                      __builtin_unreachable();
@@ -53,17 +53,28 @@ LIBC_PRINTF_MODULE((template <OverflowMode mode>
 
 #ifdef LIBC_PRINTF_DEFINE_MODULES
 #define HANDLE_OVERFLOW_MODE(MODE)                                             \
-  template int convert_float<OverflowMode::MODE>(                              \
-      Writer<OverflowMode::MODE> * writer, const FormatSection &to_conv);
+  template int convert_float<OverflowMode::MODE, char>(                        \
+      Writer<OverflowMode::MODE, char> * writer,                               \
+      const FormatSection<char> &to_conv);
 #include "src/__support/printf_core/overflow_modes.def"
 #undef HANDLE_OVERFLOW_MODE
+
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+#define HANDLE_OVERFLOW_MODE(MODE)                                             \
+  template int convert_float<OverflowMode::MODE, wchar_t>(                     \
+      Writer<OverflowMode::MODE, wchar_t> * writer,                            \
+      const FormatSection<wchar_t> &to_conv);
+#include "src/__support/printf_core/overflow_modes.def"
+#undef HANDLE_OVERFLOW_MODE
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #endif // LIBC_PRINTF_DEFINE_MODULES
 
 // convert will call a conversion function to convert the FormatSection into
 // its string representation, and then that will write the result to the
 // writer.
-template <OverflowMode mode>
-int convert(Writer<mode> *writer, const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+int convert(Writer<mode, CharT> *writer, const FormatSection<CharT> &to_conv) {
   if (!to_conv.has_conv)
     return writer->write(to_conv.raw_string);
 
@@ -71,12 +82,12 @@ int convert(Writer<mode> *writer, const FormatSection &to_conv) {
     defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
   if (to_conv.length_modifier == LengthModifier::L) {
     switch (to_conv.conv_name) {
-    case 'f':
-    case 'F':
-    case 'e':
-    case 'E':
-    case 'g':
-    case 'G':
+    case CharT{'f'}:
+    case CharT{'F'}:
+    case CharT{'e'}:
+    case CharT{'E'}:
+    case CharT{'g'}:
+    case CharT{'G'}:
       return convert_float_hex_exp(writer, to_conv);
     default:
       break;
@@ -85,48 +96,49 @@ int convert(Writer<mode> *writer, const FormatSection &to_conv) {
 #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
 
   switch (to_conv.conv_name) {
-  case '%':
-    return writer->write("%");
-  case 'c':
-    return convert_char(writer, to_conv);
-  case 's':
+  case CharT{'%'}:
+    return writer->write(CharT{'%'});
+  case CharT{'c'}:
+    return convert_character(writer, to_conv);
+  case CharT{'s'}:
     return convert_string(writer, to_conv);
-  case 'd':
-  case 'i':
-  case 'u':
-  case 'o':
-  case 'x':
-  case 'X':
-  case 'b':
-  case 'B':
+  case CharT{'d'}:
+  case CharT{'i'}:
+  case CharT{'u'}:
+  case CharT{'o'}:
+  case CharT{'x'}:
+  case CharT{'X'}:
+  case CharT{'b'}:
+  case CharT{'B'}:
     return convert_int(writer, to_conv);
 #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
-  case 'f':
-  case 'F':
-  case 'e':
-  case 'E':
-  case 'a':
-  case 'A':
-  case 'g':
-  case 'G':
+  case CharT{'f'}:
+  case CharT{'F'}:
+  case CharT{'e'}:
+  case CharT{'E'}:
+  case CharT{'a'}:
+  case CharT{'A'}:
+  case CharT{'g'}:
+  case CharT{'G'}:
     return convert_float(writer, to_conv);
 #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
 #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
-  case 'r':
-  case 'R':
-  case 'k':
-  case 'K':
+  case CharT{'r'}:
+  case CharT{'R'}:
+  case CharT{'k'}:
+  case CharT{'K'}:
     return convert_fixed(writer, to_conv);
 #endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
 #ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
-  case 'm':
+  case CharT{'m'}:
     return convert_strerror(writer, to_conv);
 #endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
 #ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
-  case 'n':
-    return convert_write_int(writer, to_conv);
+  case CharT{'n'}:
+    return convert_write_int(writer->get_chars_written(),
+                             to_conv.length_modifier, to_conv.conv_val_ptr);
 #endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
-  case 'p':
+  case CharT{'p'}:
     return convert_pointer(writer, to_conv);
   default:
     return writer->write(to_conv.raw_string);

diff  --git a/libc/src/__support/printf_core/core_structs.h b/libc/src/__support/printf_core/core_structs.h
index 60296962233224..a25b9e29ab8421 100644
--- a/libc/src/__support/printf_core/core_structs.h
+++ b/libc/src/__support/printf_core/core_structs.h
@@ -67,7 +67,7 @@ enum FormatFlags : uint8_t {
   //  locale_digits = 0x40,  // I
 };
 
-template <typename CharT> struct BasicFormatSection {
+template <typename CharT> struct FormatSection {
   bool has_conv;
 
   cpp::basic_string_view<CharT> raw_string;
@@ -86,7 +86,7 @@ template <typename CharT> struct BasicFormatSection {
 
   // This operator is only used for testing and should be automatically
   // optimized out for release builds.
-  LIBC_INLINE bool operator==(const BasicFormatSection &other) const {
+  LIBC_INLINE bool operator==(const FormatSection &other) const {
     if (has_conv != other.has_conv)
       return false;
 
@@ -112,8 +112,6 @@ template <typename CharT> struct BasicFormatSection {
   }
 };
 
-using FormatSection = BasicFormatSection<char>;
-
 enum PrimaryType : uint8_t {
   Unknown = 0,
   Float = 1,

diff  --git a/libc/src/__support/printf_core/fixed_converter.h b/libc/src/__support/printf_core/fixed_converter.h
index 04994a7729bc83..03b8b8e8d6c64a 100644
--- a/libc/src/__support/printf_core/fixed_converter.h
+++ b/libc/src/__support/printf_core/fixed_converter.h
@@ -10,7 +10,6 @@
 #define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_FIXED_CONVERTER_H
 
 #include "include/llvm-libc-macros/stdfix-macros.h"
-#include "src/__support/CPP/string_view.h"
 #include "src/__support/ctype_utils.h"
 #include "src/__support/fixed_point/fx_bits.h"
 #include "src/__support/fixed_point/fx_rep.h"
@@ -21,6 +20,10 @@
 #include "src/__support/printf_core/core_structs.h"
 #include "src/__support/printf_core/writer.h"
 
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+#include "src/__support/wctype_utils.h"
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include <inttypes.h>
 #include <stddef.h>
 
@@ -49,13 +52,13 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
 
 #define APPLY_FX_LENGTH_MODIFIER(LENGTH_MODIFIER)                              \
   do {                                                                         \
-    if (to_conv.conv_name == 'r') {                                            \
+    if (to_conv.conv_name == CharT{'r'}) {                                     \
       READ_FX_BITS(LENGTH_MODIFIER fract);                                     \
-    } else if (to_conv.conv_name == 'R') {                                     \
+    } else if (to_conv.conv_name == CharT{'R'}) {                              \
       READ_FX_BITS(unsigned LENGTH_MODIFIER fract);                            \
-    } else if (to_conv.conv_name == 'k') {                                     \
+    } else if (to_conv.conv_name == CharT{'k'}) {                              \
       READ_FX_BITS(LENGTH_MODIFIER accum);                                     \
-    } else if (to_conv.conv_name == 'K') {                                     \
+    } else if (to_conv.conv_name == CharT{'K'}) {                              \
       READ_FX_BITS(unsigned LENGTH_MODIFIER accum);                            \
     } else {                                                                   \
       LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \
@@ -63,9 +66,9 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
     }                                                                          \
   } while (false)
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_fixed(Writer<mode> *writer,
-                              const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_fixed(Writer<mode, CharT> *writer,
+                              const FormatSection<CharT> &to_conv) {
   // Long accum should be the largest type, so we can store all the smaller
   // numbers in things sized for it.
   using LARep = fixed_point::FXRep<unsigned long accum>;
@@ -123,7 +126,7 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
 
   constexpr size_t MAX_FRACTION_DIGITS = LARep::FRACTION_LEN;
 
-  char fraction_digits[MAX_FRACTION_DIGITS];
+  CharT fraction_digits[MAX_FRACTION_DIGITS];
 
   size_t valid_fraction_digits = 0;
 
@@ -142,8 +145,8 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
 
     // we add TEN_EXP_NINE to force leading zeroes to show up, then we skip the
     // first digit in the loop.
-    const IntegerToString<uint32_t> cur_fractional_digits(cur_digits +
-                                                          TEN_EXP_NINE);
+    const IntegerToString<uint32_t, radix::Dec, CharT> cur_fractional_digits(
+        cur_digits + TEN_EXP_NINE);
     for (size_t i = 0;
          i < DIGITS_PER_BLOCK && valid_fraction_digits < MAX_FRACTION_DIGITS;
          ++i, ++valid_fraction_digits)
@@ -177,7 +180,7 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
     // Handle rounding. Just do round to nearest, tie to even since it's
     // unspecified.
     RoundDirection round;
-    char first_digit_after = fraction_digits[precision];
+    CharT first_digit_after = fraction_digits[precision];
     if (internal::b36_char_to_int(first_digit_after) > 5) {
       round = RoundDirection::Up;
     } else if (internal::b36_char_to_int(first_digit_after) < 5) {
@@ -188,7 +191,7 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
       round = RoundDirection::Even;
       for (size_t cur_digit_index = precision + 1;
            cur_digit_index + 1 < valid_fraction_digits; ++cur_digit_index) {
-        if (fraction_digits[cur_digit_index] != '0') {
+        if (fraction_digits[cur_digit_index] != CharT{'0'}) {
           round = RoundDirection::Up;
           break;
         }
@@ -201,7 +204,7 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
       int digit_to_round = static_cast<int>(precision) - 1;
       for (; digit_to_round >= 0 && keep_rounding; --digit_to_round) {
         keep_rounding = false;
-        char cur_digit = fraction_digits[digit_to_round];
+        CharT cur_digit = fraction_digits[digit_to_round];
         // if the digit should not be rounded up
         if (round == RoundDirection::Even &&
             (internal::b36_char_to_int(cur_digit) % 2) == 0) {
@@ -211,8 +214,8 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
         fraction_digits[digit_to_round] += 1;
 
         // if the digit was a 9, instead replace with a 0.
-        if (cur_digit == '9') {
-          fraction_digits[digit_to_round] = '0';
+        if (cur_digit == CharT{'9'}) {
+          fraction_digits[digit_to_round] = CharT{'0'};
           keep_rounding = true;
         }
       }
@@ -230,7 +233,7 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
     valid_fraction_digits = precision;
   }
 
-  const IntegerToString<StorageType> integral_str(integral);
+  const IntegerToString<StorageType, radix::Dec, CharT> integral_str(integral);
 
   // these are signed to prevent underflow due to negative values. The
   // eventual values will always be non-negative.
@@ -241,9 +244,9 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
   if (precision > valid_fraction_digits)
     trailing_zeroes = precision - (valid_fraction_digits);
 
-  constexpr cpp::string_view DECIMAL_POINT(".");
+  constexpr CharT DECIMAL_POINT = CharT{'.'};
 
-  char sign_char = 0;
+  CharT sign_char = 0;
 
   // Check if the conv name is uppercase
   if (internal::isupper(to_conv.conv_name)) {
@@ -254,11 +257,11 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
   }
 
   if (is_negative)
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over SPACE_PREFIX
   else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
                              integral_str.size() -
@@ -278,20 +281,20 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
       RET_IF_RESULT_NEGATIVE(
           writer->write({fraction_digits, valid_fraction_digits}));
     if (trailing_zeroes > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, trailing_zeroes));
     if (padding > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding));
   } else {
     // The pattern is (spaces), (sign), (zeroes), integral, (.), (fraction),
     // (zeroes)
     if ((padding > 0) &&
         ((flags & FormatFlags::LEADING_ZEROES) != FormatFlags::LEADING_ZEROES))
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding));
     if (sign_char > 0)
       RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
     if ((padding > 0) &&
         ((flags & FormatFlags::LEADING_ZEROES) == FormatFlags::LEADING_ZEROES))
-      RET_IF_RESULT_NEGATIVE(writer->write('0', padding));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, padding));
     RET_IF_RESULT_NEGATIVE(writer->write(integral_str.view()));
     if (has_decimal_point)
       RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
@@ -299,7 +302,7 @@ LIBC_INLINE int convert_fixed(Writer<mode> *writer,
       RET_IF_RESULT_NEGATIVE(
           writer->write({fraction_digits, valid_fraction_digits}));
     if (trailing_zeroes > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, trailing_zeroes));
   }
   return WRITE_OK;
 }

diff  --git a/libc/src/__support/printf_core/float_dec_converter.h b/libc/src/__support/printf_core/float_dec_converter.h
index 974765a9667fe9..38746b411578c5 100644
--- a/libc/src/__support/printf_core/float_dec_converter.h
+++ b/libc/src/__support/printf_core/float_dec_converter.h
@@ -23,15 +23,22 @@
 #include "src/__support/printf_core/float_inf_nan_converter.h"
 #include "src/__support/printf_core/writer.h"
 
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+#include "src/__support/wctype_utils.h"
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include <inttypes.h>
 #include <stddef.h>
 
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-using DecimalString = IntegerToString<intmax_t>;
+template <typename CharT>
+using DecimalString = IntegerToString<intmax_t, radix::Dec, CharT>;
+
+template <typename CharT>
 using ExponentString =
-    IntegerToString<intmax_t, radix::Dec::WithWidth<2>::WithSign>;
+    IntegerToString<intmax_t, radix::Dec::WithWidth<2>::WithSign, CharT>;
 
 // Returns true if value is divisible by 2^p.
 template <typename T>
@@ -41,8 +48,8 @@ multiple_of_power_of_2(T value, uint32_t p) {
   return (value & ((T(1) << p) - 1)) == 0;
 }
 
-constexpr size_t BLOCK_SIZE = 9;
-constexpr uint32_t MAX_BLOCK = 999999999;
+LIBC_INLINE_VAR constexpr size_t BLOCK_SIZE = 9;
+LIBC_INLINE_VAR constexpr uint32_t MAX_BLOCK = 999999999;
 
 // constexpr size_t BLOCK_SIZE = 18;
 // constexpr uint32_t MAX_BLOCK = 999999999999999999;
@@ -99,21 +106,22 @@ zero_after_digits(int32_t base_2_exp, int32_t digits_after_point, T mantissa,
   return has_trailing_zeros;
 }
 
-template <OverflowMode mode> class PaddingWriter {
+template <OverflowMode mode, typename CharT> class PaddingWriter {
   bool left_justified = false;
   bool leading_zeroes = false;
-  char sign_char = 0;
+  CharT sign_char = 0;
   size_t min_width = 0;
 
 public:
   LIBC_INLINE PaddingWriter() {}
-  LIBC_INLINE PaddingWriter(const FormatSection &to_conv, char init_sign_char)
+  LIBC_INLINE PaddingWriter(const FormatSection<CharT> &to_conv,
+                            CharT init_sign_char)
       : left_justified((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) > 0),
         leading_zeroes((to_conv.flags & FormatFlags::LEADING_ZEROES) > 0),
         sign_char(init_sign_char),
         min_width(to_conv.min_width > 0 ? to_conv.min_width : 0) {}
 
-  LIBC_INLINE int write_left_padding(Writer<mode> *writer,
+  LIBC_INLINE int write_left_padding(Writer<mode, CharT> *writer,
                                      size_t total_digits) {
     // The pattern is (spaces) (sign) (zeroes), but only one of spaces and
     // zeroes can be written, and only if the padding amount is positive.
@@ -126,25 +134,25 @@ template <OverflowMode mode> class PaddingWriter {
       return 0;
     }
     if (!leading_zeroes) {
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_amount));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding_amount));
     }
     if (sign_char > 0) {
       RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
     }
     if (leading_zeroes) {
-      RET_IF_RESULT_NEGATIVE(writer->write('0', padding_amount));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, padding_amount));
     }
     return 0;
   }
 
-  LIBC_INLINE int write_right_padding(Writer<mode> *writer,
+  LIBC_INLINE int write_right_padding(Writer<mode, CharT> *writer,
                                       size_t total_digits) {
     // If and only if the conversion is left justified, there may be trailing
     // spaces.
     int padding_amount =
         static_cast<int>(min_width - total_digits - (sign_char > 0 ? 1 : 0));
     if (left_justified && padding_amount > 0) {
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_amount));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding_amount));
     }
     return 0;
   }
@@ -163,22 +171,22 @@ template <OverflowMode mode> class PaddingWriter {
   This FloatWriter class does the buffering and counting, and writes to the
   output when necessary.
 */
-template <OverflowMode mode> class FloatWriter {
-  char block_buffer[BLOCK_SIZE]; // The buffer that holds a block.
-  size_t buffered_digits = 0;    // The number of digits held in the buffer.
+template <OverflowMode mode, typename CharT> class FloatWriter {
+  CharT block_buffer[BLOCK_SIZE]; // The buffer that holds a block.
   bool has_written = false;      // True once any digits have been output.
+  bool has_decimal_point;        // True if the number has a decimal point.
+  size_t buffered_digits = 0;    // The number of digits held in the buffer.
   size_t max_block_count = 0; // The # of blocks of all 9s currently buffered.
   size_t total_digits = 0;    // The number of digits that will be output.
   size_t digits_before_decimal = 0; // The # of digits to write before the '.'
   size_t total_digits_written = 0;  // The # of digits that have been output.
-  bool has_decimal_point;           // True if the number has a decimal point.
-  Writer<mode> *writer;             // Writes to the final output.
-  PaddingWriter<mode>
+  Writer<mode, CharT> *writer;      // Writes to the final output.
+  PaddingWriter<mode, CharT>
       padding_writer; // Handles prefixes/padding, uses total_digits.
 
   LIBC_INLINE int flush_buffer(bool round_up_max_blocks = false) {
-    const char MAX_BLOCK_DIGIT = (round_up_max_blocks ? '0' : '9');
-    constexpr char DECIMAL_POINT = '.';
+    const CharT MAX_BLOCK_DIGIT = CharT{round_up_max_blocks ? '0' : '9'};
+    constexpr CharT DECIMAL_POINT = CharT{'.'};
 
     // Write the most recent buffered block, and mark has_written
     if (!has_written) {
@@ -255,9 +263,9 @@ template <OverflowMode mode> class FloatWriter {
 #endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
 public:
-  LIBC_INLINE FloatWriter(Writer<mode> *init_writer,
+  LIBC_INLINE FloatWriter(Writer<mode, CharT> *init_writer,
                           bool init_has_decimal_point,
-                          const PaddingWriter<mode> &init_padding_writer)
+                          const PaddingWriter<mode, CharT> &init_padding_writer)
       : has_decimal_point(init_has_decimal_point), writer(init_writer),
         padding_writer(init_padding_writer) {}
 
@@ -268,8 +276,8 @@ template <OverflowMode mode> class FloatWriter {
   }
 
   LIBC_INLINE void write_first_block(BlockInt block, bool exp_format = false) {
-    const DecimalString buf(block);
-    const cpp::string_view int_to_str = buf.view();
+    const DecimalString<CharT> buf(block);
+    const cpp::basic_string_view<CharT> int_to_str = buf.view();
     size_t digits_buffered = int_to_str.size();
     // Block Buffer is guaranteed to not overflow since block cannot have more
     // than BLOCK_SIZE digits.
@@ -297,8 +305,8 @@ template <OverflowMode mode> class FloatWriter {
       // Now buffer the current block. We add 1 + MAX_BLOCK to force the
       // leading zeroes, and drop the leading one. This is probably inefficient,
       // but it works. See https://xkcd.com/2021/
-      const DecimalString buf(block + (MAX_BLOCK + 1));
-      const cpp::string_view int_to_str = buf.view();
+      const DecimalString<CharT> buf(block + (MAX_BLOCK + 1));
+      const cpp::basic_string_view<CharT> int_to_str = buf.view();
       // TODO: Replace with memcpy
       for (size_t count = 0; count < BLOCK_SIZE; ++count) {
         block_buffer[count] = int_to_str[count + 1];
@@ -311,14 +319,14 @@ template <OverflowMode mode> class FloatWriter {
 
   LIBC_INLINE int write_last_block(BlockInt block, size_t block_digits,
                                    RoundDirection round, int exponent = 0,
-                                   char exp_char = '\0') {
+                                   CharT exp_char = CharT{'\0'}) {
     bool has_exp = (exp_char != '\0');
 
-    char end_buff[BLOCK_SIZE];
+    CharT end_buff[BLOCK_SIZE];
 
     {
-      const DecimalString buf(block + (MAX_BLOCK + 1));
-      const cpp::string_view int_to_str = buf.view();
+      const DecimalString<CharT> buf(block + (MAX_BLOCK + 1));
+      const cpp::basic_string_view<CharT> int_to_str = buf.view();
 
       // copy the last block_digits characters into the start of end_buff.
       // TODO: Replace with memcpy
@@ -327,11 +335,11 @@ template <OverflowMode mode> class FloatWriter {
       }
     }
 
-    char low_digit = '0';
+    CharT low_digit = CharT{'0'};
     if (block_digits > 0) {
       low_digit = end_buff[block_digits - 1];
     } else if (max_block_count > 0) {
-      low_digit = '9';
+      low_digit = CharT{'9'};
     } else if (buffered_digits > 0) {
       low_digit = block_buffer[buffered_digits - 1];
     }
@@ -348,8 +356,8 @@ template <OverflowMode mode> class FloatWriter {
       // handle the low block that we're adding
       for (int count = static_cast<int>(block_digits) - 1;
            count >= 0 && has_carry; --count) {
-        if (end_buff[count] == '9') {
-          end_buff[count] = '0';
+        if (end_buff[count] == CharT{'9'}) {
+          end_buff[count] = CharT{'0'};
         } else {
           end_buff[count] += 1;
           has_carry = false;
@@ -360,8 +368,8 @@ template <OverflowMode mode> class FloatWriter {
       // handle the high block that's buffered
       for (int count = static_cast<int>(buffered_digits) - 1;
            count >= 0 && has_carry; --count) {
-        if (block_buffer[count] == '9') {
-          block_buffer[count] = '0';
+        if (block_buffer[count] == CharT{'9'}) {
+          block_buffer[count] = CharT{'0'};
         } else {
           block_buffer[count] += 1;
           has_carry = false;
@@ -371,15 +379,15 @@ template <OverflowMode mode> class FloatWriter {
       // has_carry should only be true here if every previous digit is 9, which
       // implies that the number has never been written.
       if (has_carry /* && !has_written */) {
-        constexpr char DECIMAL_POINT = '.';
+        constexpr CharT DECIMAL_POINT = CharT{'.'};
 
         if (has_exp) { // This is in %e style
           // Since this is exponential notation, we don't write any more digits
           // but we do increment the exponent.
           ++exponent;
 
-          const ExponentString buf(exponent);
-          const cpp::string_view int_to_str = buf.view();
+          const ExponentString<CharT> buf(exponent);
+          const cpp::basic_string_view<CharT> int_to_str = buf.view();
 
           // TODO: also change this to calculate the width of the number more
           // efficiently.
@@ -400,7 +408,7 @@ template <OverflowMode mode> class FloatWriter {
               padding_writer.write_left_padding(writer, total_digits));
           // Now we know we need to print a leading 1, the decimal point, and
           // then zeroes after it.
-          RET_IF_RESULT_NEGATIVE(writer->write('1'));
+          RET_IF_RESULT_NEGATIVE(writer->write(CharT{'1'}));
           // digits_before_decimal - 1 to account for the leading '1'
           if (has_decimal_point) {
             RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
@@ -408,7 +416,8 @@ template <OverflowMode mode> class FloatWriter {
             // point, or exponent.
 
             if (number_digits > 1) {
-              RET_IF_RESULT_NEGATIVE(writer->write('0', number_digits - 1));
+              RET_IF_RESULT_NEGATIVE(
+                  writer->write(CharT{'0'}, number_digits - 1));
             }
           }
           RET_IF_RESULT_NEGATIVE(writer->write(exp_char));
@@ -426,16 +435,17 @@ template <OverflowMode mode> class FloatWriter {
               padding_writer.write_left_padding(writer, total_digits));
           // Now we know we need to print a leading 1, zeroes up to the decimal
           // point, the decimal point, and then finally digits after it.
-          RET_IF_RESULT_NEGATIVE(writer->write('1'));
+          RET_IF_RESULT_NEGATIVE(writer->write(CharT{'1'}));
           // digits_before_decimal - 1 to account for the leading '1'
-          RET_IF_RESULT_NEGATIVE(writer->write('0', digits_before_decimal - 1));
+          RET_IF_RESULT_NEGATIVE(
+              writer->write(CharT{'0'}, digits_before_decimal - 1));
           if (has_decimal_point) {
             RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
             // add one to digits_before_decimal to account for the decimal point
             // itself.
             if (total_digits > digits_before_decimal + 1) {
               RET_IF_RESULT_NEGATIVE(writer->write(
-                  '0', total_digits - (digits_before_decimal + 1)));
+                  CharT{'0'}, total_digits - (digits_before_decimal + 1)));
             }
           }
           total_digits_written = total_digits;
@@ -460,7 +470,7 @@ template <OverflowMode mode> class FloatWriter {
 
     if (has_exp) {
       RET_IF_RESULT_NEGATIVE(writer->write(exp_char));
-      const ExponentString buf(exponent);
+      const ExponentString<CharT> buf(exponent);
       RET_IF_RESULT_NEGATIVE(writer->write(buf.view()));
     }
     total_digits_written = total_digits;
@@ -470,7 +480,7 @@ template <OverflowMode mode> class FloatWriter {
 
   LIBC_INLINE int write_zeroes(uint32_t num_zeroes) {
     RET_IF_RESULT_NEGATIVE(flush_buffer());
-    RET_IF_RESULT_NEGATIVE(writer->write('0', num_zeroes));
+    RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, num_zeroes));
     return 0;
   }
 
@@ -479,33 +489,29 @@ template <OverflowMode mode> class FloatWriter {
   }
 };
 
-// Class-template auto deduction helpers, add more if needed.
-template <OverflowMode mode>
-FloatWriter(Writer<mode>, bool, const PaddingWriter<mode>) -> FloatWriter<mode>;
-
 // This implementation is based on the Ryu Printf algorithm by Ulf Adams:
 // Ulf Adams. 2019. Ryƫ revisited: printf floating point conversion.
 // Proc. ACM Program. Lang. 3, OOPSLA, Article 169 (October 2019), 23 pages.
 // https://doi.org/10.1145/3360595
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_finite_float_decimal_typed(Writer<mode> *writer,
-                                   const FormatSection &to_conv,
+convert_finite_float_decimal_typed(Writer<mode, CharT> *writer,
+                                   const FormatSection<CharT> &to_conv,
                                    fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
   constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
   int exponent = float_bits.get_explicit_exponent();
 
-  char sign_char = 0;
+  CharT sign_char = 0;
 
   if (float_bits.is_neg())
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over SPACE_PREFIX
   else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
            FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   // If to_conv doesn't specify a precision, the precision defaults to 6.
   const unsigned int precision = to_conv.precision < 0 ? 6 : to_conv.precision;
@@ -517,8 +523,9 @@ convert_finite_float_decimal_typed(Writer<mode> *writer,
   // ignored.
   bool nonzero = false;
 
-  PaddingWriter<mode> padding_writer(to_conv, sign_char);
-  FloatWriter float_writer(writer, has_decimal_point, padding_writer);
+  PaddingWriter<mode, CharT> padding_writer(to_conv, sign_char);
+  FloatWriter<mode, CharT> float_writer(writer, has_decimal_point,
+                                        padding_writer);
   FloatToString<T> float_converter(float_bits.get_val());
 
   const size_t positive_blocks = float_converter.get_positive_blocks();
@@ -598,33 +605,34 @@ convert_finite_float_decimal_typed(Writer<mode> *writer,
   return WRITE_OK;
 }
 
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_finite_float_dec_exp_typed(Writer<mode> *writer,
-                                   const FormatSection &to_conv,
+convert_finite_float_dec_exp_typed(Writer<mode, CharT> *writer,
+                                   const FormatSection<CharT> &to_conv,
                                    fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
   constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
   int exponent = float_bits.get_explicit_exponent();
 
-  char sign_char = 0;
+  CharT sign_char = 0;
 
   if (float_bits.is_neg())
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over SPACE_PREFIX
   else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
            FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   // If to_conv doesn't specify a precision, the precision defaults to 6.
   const unsigned int precision = to_conv.precision < 0 ? 6 : to_conv.precision;
   bool has_decimal_point =
       (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
 
-  PaddingWriter<mode> padding_writer(to_conv, sign_char);
-  FloatWriter float_writer(writer, has_decimal_point, padding_writer);
+  PaddingWriter<mode, CharT> padding_writer(to_conv, sign_char);
+  FloatWriter<mode, CharT> float_writer(writer, has_decimal_point,
+                                        padding_writer);
   FloatToString<T> float_converter(float_bits.get_val());
 
   size_t digits_written = 0;
@@ -655,13 +663,13 @@ convert_finite_float_dec_exp_typed(Writer<mode> *writer,
     cur_block = 0;
   }
 
-  const size_t block_width = IntegerToString<intmax_t>(digits).size();
+  const size_t block_width = DecimalString<CharT>(digits).size();
 
   final_exponent = static_cast<int>(cur_block * BLOCK_SIZE) +
                    static_cast<int>(block_width - 1);
   int positive_exponent = final_exponent < 0 ? -final_exponent : final_exponent;
 
-  size_t exponent_width = IntegerToString<intmax_t>(positive_exponent).size();
+  size_t exponent_width = DecimalString<CharT>(positive_exponent).size();
 
   // Calculate the total number of digits in the number.
   // 1 - the digit before the decimal point
@@ -696,7 +704,7 @@ convert_finite_float_dec_exp_typed(Writer<mode> *writer,
 
   // if the last block is also the first block, then ignore leading zeroes.
   if (digits_written == 0) {
-    last_block_size = IntegerToString<intmax_t>(digits).size();
+    last_block_size = DecimalString<CharT>(digits).size();
   }
 
   // This tracks if the number is truncated, that meaning that the digits after
@@ -754,17 +762,17 @@ convert_finite_float_dec_exp_typed(Writer<mode> *writer,
 
   RET_IF_RESULT_NEGATIVE(float_writer.write_last_block(
       digits, maximum, round, final_exponent,
-      internal::islower(to_conv.conv_name) ? 'e' : 'E'));
+      internal::islower(to_conv.conv_name) ? CharT{'e'} : CharT{'E'}));
 
   RET_IF_RESULT_NEGATIVE(float_writer.right_pad());
   return WRITE_OK;
 }
 
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_finite_float_dec_auto_typed(Writer<mode> *writer,
-                                    const FormatSection &to_conv,
+convert_finite_float_dec_auto_typed(Writer<mode, CharT> *writer,
+                                    const FormatSection<CharT> &to_conv,
                                     fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
   constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
@@ -813,7 +821,7 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
     // In the case of 0.0, then it's always decimal format. If we don't have alt
     // form then the trailing zeroes are trimmed to make "0", else the precision
     // is 1 less than specified by the user.
-    FormatSection new_conv = to_conv;
+    FormatSection<CharT> new_conv = to_conv;
     if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
       // This is a style F conversion, making the precision P - 1 - X, but since
       // this is for the number 0, X (the base 10 exponent) is always 0.
@@ -824,7 +832,7 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
     return convert_finite_float_decimal_typed<T>(writer, new_conv, float_bits);
   }
 
-  const size_t block_width = IntegerToString<intmax_t>(digits).size();
+  const size_t block_width = DecimalString<CharT>(digits).size();
 
   size_t digits_checked = 0;
   // TODO: look into unifying trailing_zeroes and trailing_nines. The number can
@@ -837,14 +845,14 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
 
   // If the first block is not also the last block
   if (block_width <= exp_precision + 1) {
-    const DecimalString buf(digits);
-    const cpp::string_view int_to_str = buf.view();
+    const DecimalString<CharT> buf(digits);
+    const cpp::basic_string_view<CharT> int_to_str = buf.view();
 
     for (size_t i = 0; i < block_width; ++i) {
-      if (int_to_str[i] == '9') {
+      if (int_to_str[i] == CharT{'9'}) {
         ++trailing_nines;
         trailing_zeroes = 0;
-      } else if (int_to_str[i] == '0') {
+      } else if (int_to_str[i] == CharT{'0'}) {
         ++trailing_zeroes;
         trailing_nines = 0;
       } else {
@@ -899,8 +907,8 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
 
   size_t last_block_size = BLOCK_SIZE;
 
-  const DecimalString buf(digits);
-  const cpp::string_view int_to_str = buf.view();
+  const DecimalString<CharT> buf(digits);
+  const cpp::basic_string_view<CharT> int_to_str = buf.view();
 
   size_t implicit_leading_zeroes = BLOCK_SIZE - int_to_str.size();
 
@@ -934,10 +942,10 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
 
   // Check the upper digits of this block.
   for (int i = 0; i < digits_to_check; ++i) {
-    if (int_to_str[i] == '9') {
+    if (int_to_str[i] == CharT{'9'}) {
       ++trailing_nines;
       trailing_zeroes = 0;
-    } else if (int_to_str[i] == '0') {
+    } else if (int_to_str[i] == CharT{'0'}) {
       ++trailing_zeroes;
       trailing_nines = 0;
     } else {
@@ -1067,7 +1075,7 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
   // if P > X >= -4, the conversion is with style f (or F) and precision equals
   //  P - (X + 1).
   if (static_cast<int>(init_precision) > base_10_exp && base_10_exp >= -4) {
-    FormatSection new_conv = to_conv;
+    FormatSection<CharT> new_conv = to_conv;
     const int conv_precision = init_precision - (base_10_exp + 1);
 
     if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
@@ -1108,7 +1116,7 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
     // otherwise, the conversion is with style e (or E) and precision equals
     // P - 1
     const int conv_precision = init_precision - 1;
-    FormatSection new_conv = to_conv;
+    FormatSection<CharT> new_conv = to_conv;
     if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
       new_conv.precision = conv_precision;
     } else {
@@ -1129,9 +1137,9 @@ convert_finite_float_dec_auto_typed(Writer<mode> *writer,
 
 // TODO: unify the float converters to remove the duplicated checks for inf/nan.
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_decimal(Writer<mode> *writer,
-                                      const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_decimal(Writer<mode, CharT> *writer,
+                                      const FormatSection<CharT> &to_conv) {
   InfNanFPBitsProperties inf_nan_properties;
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   if (to_conv.length_modifier == LengthModifier::Q) {
@@ -1174,9 +1182,9 @@ LIBC_INLINE int convert_float_decimal(Writer<mode> *writer,
   return convert_inf_nan(writer, inf_nan_properties, to_conv);
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_dec_exp(Writer<mode> *writer,
-                                      const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_dec_exp(Writer<mode, CharT> *writer,
+                                      const FormatSection<CharT> &to_conv) {
   InfNanFPBitsProperties inf_nan_properties;
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   if (to_conv.length_modifier == LengthModifier::Q) {
@@ -1219,9 +1227,9 @@ LIBC_INLINE int convert_float_dec_exp(Writer<mode> *writer,
   return convert_inf_nan(writer, inf_nan_properties, to_conv);
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_dec_auto(Writer<mode> *writer,
-                                       const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_dec_auto(Writer<mode, CharT> *writer,
+                                       const FormatSection<CharT> &to_conv) {
   InfNanFPBitsProperties inf_nan_properties;
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   if (to_conv.length_modifier == LengthModifier::Q) {

diff  --git a/libc/src/__support/printf_core/float_dec_converter_limited.h b/libc/src/__support/printf_core/float_dec_converter_limited.h
index afdfd68a513793..39e332ff91714f 100644
--- a/libc/src/__support/printf_core/float_dec_converter_limited.h
+++ b/libc/src/__support/printf_core/float_dec_converter_limited.h
@@ -47,6 +47,7 @@
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/dyadic_float.h"
 #include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/ctype_utils.h"
 #include "src/__support/integer_to_string.h"
 #include "src/__support/libc_assert.h"
 #include "src/__support/macros/config.h"
@@ -55,6 +56,10 @@
 #include "src/__support/printf_core/writer.h"
 #include "src/string/memory_utils/inline_memcpy.h"
 
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+#include "src/__support/wctype_utils.h"
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
@@ -91,7 +96,7 @@ struct DigitsInput {
   }
 };
 
-struct DigitsOutput {
+template <typename CharT> struct DigitsOutput {
   // Output from decimal_digits().
   //
   // `digits` is a buffer containing nothing but ASCII digits. Even if the
@@ -104,7 +109,7 @@ struct DigitsOutput {
   // and exponent = 3 then this represents 1.234e3, or just the integer 1234.
   size_t ndigits;
   int exponent;
-  char digits[MAX_DIGITS + 1];
+  CharT digits[MAX_DIGITS + 1];
 };
 
 // Estimate log10 of a power of 2, by multiplying its exponent by
@@ -127,20 +132,21 @@ LIBC_INLINE int estimate_log10(int exponent_of_2) {
 // `decimal_digits` has a place value of _at least_ 10^-precision. But also, at
 // most `MAX_DIGITS` digits are returned, so the caller may need to pad it at
 // the end with the appropriate number of extra 0s.
-LIBC_INLINE
-DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
+template <typename CharT>
+LIBC_INLINE DigitsOutput<CharT> decimal_digits(DigitsInput input, int precision,
+                                               bool e_mode) {
   if (input.mantissa == 0) {
-    // Special-case zero, by manually generating the right number of zero
-    // digits and setting an appropriate exponent.
-    DigitsOutput output;
+    // Special-case zero, by manually setting the right number of zero digits
+    // and setting an appropriate exponent.
+    DigitsOutput<CharT> output = {};
     if (!e_mode) {
       // In F mode, it's enough to return an empty string of digits. That's the
       // same thing we do when given a nonzero number that rounds down to 0.
       output.ndigits = 0;
       output.exponent = -precision - 1;
     } else {
-      // In E mode, generate a string containing the expected number of 0s.
-      __builtin_memset(output.digits, '0', precision);
+      // In E mode, set the expected number of 0s (output.digits has been
+      // value-initialized to all zeroes).
       output.ndigits = precision;
       output.exponent = 0;
     }
@@ -247,7 +253,7 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
   // Start making the output struct, by copying in the digits from the above
   // object. At this stage we may also have one digit too many (but that's OK,
   // there's space for it in the DigitsOutput buffer).
-  DigitsOutput output;
+  DigitsOutput<CharT> output;
   output.ndigits = view.size();
   inline_memcpy(output.digits, view.data(), output.ndigits);
 
@@ -360,12 +366,12 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
       // at a time. (A bit painful, but better than going back to the integer
       // we made it from and doing the decimal conversion all over again.)
       for (size_t i = output.ndigits; i-- > 0;) {
-        if (output.digits[i] != '9') {
+        if (output.digits[i] != CharT{'9'}) {
           output.digits[i] = internal::int_to_b36_char(
               internal::b36_char_to_int(output.digits[i]) + 1);
           break;
         } else {
-          output.digits[i] = '0';
+          output.digits[i] = CharT{'0'};
         }
       }
     }
@@ -374,26 +380,26 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
   return output;
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
-                                           const FormatSection &to_conv,
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_finite_float_inner(Writer<mode, CharT> *writer,
+                                           const FormatSection<CharT> &to_conv,
                                            int32_t fraction_len, int exponent,
                                            AnyFloatStorageType mantissa,
                                            Sign sign, ConversionType ctype) {
-  constexpr char DECIMAL_POINT = '.';
+  constexpr CharT DECIMAL_POINT = CharT{'.'};
   // If to_conv doesn't specify a precision, the precision defaults to 6.
   unsigned precision = to_conv.precision < 0 ? 6 : to_conv.precision;
 
   // Decide if we're displaying a sign character, depending on the format flags
   // and whether the input is negative.
-  char sign_char = 0;
+  CharT sign_char = 0;
   if (sign.is_neg())
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over SPACE_PREFIX
   else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
            FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   // Prepare the input to decimal_digits().
   DigitsInput input(fraction_len, mantissa, exponent, sign);
@@ -403,7 +409,7 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
   // in the following variables:
 
   // The decimal digits, and the exponent of the topmost one.
-  DigitsOutput output;
+  DigitsOutput<CharT> output;
   // The start and end of the digit string we're displaying, as indices into
   // `output.digits`. The indices may be out of bounds in either direction, in
   // which case digits beyond the bounds of the buffer should be displayed as
@@ -424,7 +430,8 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
     // (`%.6e` means six digits _after_ the decimal point, like 1.123456e+00).
     //
     // Also, bound the number of digits we request at MAX_DIGITS.
-    output = decimal_digits(input, cpp::min(precision + 1, MAX_DIGITS), true);
+    output =
+        decimal_digits<CharT>(input, cpp::min(precision + 1, MAX_DIGITS), true);
 
     // We display digits from the start of the buffer, and always output
     // `precision+1` of them (which will append zeroes if the user requested
@@ -441,7 +448,7 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
   case ConversionType::F:
     // In F mode, we provide decimal_digits() with the unmodified input
     // precision, and let it give us as many digits as we can.
-    output = decimal_digits(input, precision, false);
+    output = decimal_digits<CharT>(input, precision, false);
 
     // Initialize (start, limit) to display everything from the first nonzero
     // digit (necessarily at the start of the output buffer) to the digit at
@@ -470,7 +477,8 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
     //
     // Also, a precision of 0 is treated the same as 1.
     precision = cpp::max(precision, 1u);
-    output = decimal_digits(input, cpp::min(precision, MAX_DIGITS), true);
+    output =
+        decimal_digits<CharT>(input, cpp::min(precision, MAX_DIGITS), true);
 
     // As in E mode, we default to displaying precisely the digits in the
     // output buffer.
@@ -486,7 +494,7 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
       limit = cpp::min(limit, int(output.ndigits));
 
       // Then check the digits in the buffer and remove as many as possible.
-      while (limit > 1 && output.digits[limit - 1] == '0')
+      while (limit > 1 && output.digits[limit - 1] == CharT{'0'})
         limit--;
     }
 
@@ -543,14 +551,14 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
 
   // Format the exponent suffix (e+NN, e-NN) into a buffer, or leave the buffer
   // empty if we're not displaying one.
-  char expbuf[16]; // more than enough space for e+NNNN
+  CharT expbuf[16]; // more than enough space for e+NNNN
   size_t explen = 0;
   if (show_exponent) {
     const IntegerToString<decltype(output.exponent),
                           radix::Dec::WithWidth<2>::WithSign>
         expcvt{output.exponent};
     cpp::string_view expview = expcvt.view();
-    expbuf[0] = internal::islower(to_conv.conv_name) ? 'e' : 'E';
+    expbuf[0] = internal::islower(to_conv.conv_name) ? CharT{'e'} : CharT{'E'};
     explen = expview.size() + 1;
     inline_memcpy(expbuf + 1, expview.data(), expview.size());
   }
@@ -583,7 +591,7 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
 
   // Leading-space padding, if any
   if (padding == Padding::LeadingSpace)
-    RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_amount));
+    RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding_amount));
 
   // Sign, if any
   if (sign_char)
@@ -591,7 +599,7 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
 
   // Zero padding, if any
   if (padding == Padding::Zero)
-    RET_IF_RESULT_NEGATIVE(writer->write('0', padding_amount));
+    RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, padding_amount));
 
   // Mantissa digits, maybe with a decimal point
   for (int pos = start; pos < limit; ++pos) {
@@ -600,7 +608,7 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
       RET_IF_RESULT_NEGATIVE(writer->write(output.digits[pos]));
     } else {
       // This digit is outside the buffer, so write a zero
-      RET_IF_RESULT_NEGATIVE(writer->write('0'));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}));
     }
 
     // Show the decimal point, if this is the digit it comes after
@@ -613,25 +621,26 @@ LIBC_INLINE int convert_finite_float_inner(Writer<mode> *writer,
 
   // Trailing-space padding, if any
   if (padding == Padding::TrailingSpace)
-    RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_amount));
+    RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding_amount));
 
   return WRITE_OK;
 }
 
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int
-convert_finite_float_typed(Writer<mode> *writer, const FormatSection &to_conv,
-                           fputil::FPBits<T> float_bits, ConversionType ctype) {
+LIBC_INLINE int convert_finite_float_typed(Writer<mode, CharT> *writer,
+                                           const FormatSection<CharT> &to_conv,
+                                           fputil::FPBits<T> float_bits,
+                                           ConversionType ctype) {
   return convert_finite_float_inner(writer, to_conv, float_bits.FRACTION_LEN,
                                     float_bits.get_explicit_exponent(),
                                     float_bits.get_explicit_mantissa(),
                                     float_bits.sign(), ctype);
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_outer(Writer<mode> *writer,
-                                    const FormatSection &to_conv,
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_outer(Writer<mode, CharT> *writer,
+                                    const FormatSection<CharT> &to_conv,
                                     ConversionType ctype) {
   InfNanFPBitsProperties inf_nan_properties;
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
@@ -675,51 +684,51 @@ LIBC_INLINE int convert_float_outer(Writer<mode> *writer,
   return convert_inf_nan(writer, inf_nan_properties, to_conv);
 }
 
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_finite_float_decimal_typed(Writer<mode> *writer,
-                                   const FormatSection &to_conv,
+convert_finite_float_decimal_typed(Writer<mode, CharT> *writer,
+                                   const FormatSection<CharT> &to_conv,
                                    fputil::FPBits<T> float_bits) {
   return convert_finite_float_typed<T>(writer, to_conv, float_bits,
                                        ConversionType::F);
 }
 
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_finite_float_dec_exp_typed(Writer<mode> *writer,
-                                   const FormatSection &to_conv,
+convert_finite_float_dec_exp_typed(Writer<mode, CharT> *writer,
+                                   const FormatSection<CharT> &to_conv,
                                    fputil::FPBits<T> float_bits) {
   return convert_finite_float_typed<T>(writer, to_conv, float_bits,
                                        ConversionType::E);
 }
 
-template <typename T, OverflowMode mode,
+template <typename T, OverflowMode mode, typename CharT,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_finite_float_dec_auto_typed(Writer<mode> *writer,
-                                    const FormatSection &to_conv,
+convert_finite_float_dec_auto_typed(Writer<mode, CharT> *writer,
+                                    const FormatSection<CharT> &to_conv,
                                     fputil::FPBits<T> float_bits) {
   return convert_finite_float_typed<T>(writer, to_conv, float_bits,
                                        ConversionType::G);
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_decimal(Writer<mode> *writer,
-                                      const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_decimal(Writer<mode, CharT> *writer,
+                                      const FormatSection<CharT> &to_conv) {
   return convert_float_outer(writer, to_conv, ConversionType::F);
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_dec_exp(Writer<mode> *writer,
-                                      const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_dec_exp(Writer<mode, CharT> *writer,
+                                      const FormatSection<CharT> &to_conv) {
   return convert_float_outer(writer, to_conv, ConversionType::E);
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_dec_auto(Writer<mode> *writer,
-                                       const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_dec_auto(Writer<mode, CharT> *writer,
+                                       const FormatSection<CharT> &to_conv) {
   return convert_float_outer(writer, to_conv, ConversionType::G);
 }
 

diff  --git a/libc/src/__support/printf_core/float_hex_converter.h b/libc/src/__support/printf_core/float_hex_converter.h
index ee751901dda1e8..156f87518a0de0 100644
--- a/libc/src/__support/printf_core/float_hex_converter.h
+++ b/libc/src/__support/printf_core/float_hex_converter.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_FLOAT_HEX_CONVERTER_H
 
 #include "src/__support/CPP/string_view.h"
+#include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/rounding_mode.h"
 #include "src/__support/ctype_utils.h"
@@ -19,6 +20,10 @@
 #include "src/__support/printf_core/float_inf_nan_converter.h"
 #include "src/__support/printf_core/writer.h"
 
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+#include "src/__support/wctype_utils.h"
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include <inttypes.h>
 #include <stddef.h>
 
@@ -48,31 +53,31 @@ get_float_hex_exp_fp_bits_properties_typed(fputil::FPBits<T> float_bits) {
 }
 
 // Returns relevant floating point properties for conversion using
-// `to_conv.length_modifier` to infer the conversion value type.
-LIBC_INLINE FloatHexExpFPBitsProperties
-get_float_hex_exp_fp_bits_properties_lm(const FormatSection &to_conv) {
+// `length_modifier` to infer the conversion value type.
+LIBC_INLINE FloatHexExpFPBitsProperties get_float_hex_exp_fp_bits_properties_lm(
+    LengthModifier length_modifier, AnyFloatStorageType conv_val_raw) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
-  if (to_conv.length_modifier == LengthModifier::Q)
+  if (length_modifier == LengthModifier::Q)
     return get_float_hex_exp_fp_bits_properties_typed<float128>(
-        fputil::FPBits<float128>(to_conv.conv_val_raw));
+        fputil::FPBits<float128>(conv_val_raw));
 #endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
 #ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  if (to_conv.length_modifier == LengthModifier::L)
+  if (length_modifier == LengthModifier::L)
     return get_float_hex_exp_fp_bits_properties_typed<long double>(
         fputil::FPBits<long double>(
             static_cast<fputil::FPBits<long double>::StorageType>(
-                to_conv.conv_val_raw)));
+                conv_val_raw)));
 #endif // !LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
   return get_float_hex_exp_fp_bits_properties_typed<double>(
-      fputil::FPBits<double>(static_cast<fputil::FPBits<double>::StorageType>(
-          to_conv.conv_val_raw)));
+      fputil::FPBits<double>(
+          static_cast<fputil::FPBits<double>::StorageType>(conv_val_raw)));
 }
 
-template <OverflowMode mode>
+template <OverflowMode mode, typename CharT>
 LIBC_INLINE int
-convert_finite_float_hex_exp(Writer<mode> *writer,
+convert_finite_float_hex_exp(Writer<mode, CharT> *writer,
                              FloatHexExpFPBitsProperties fp_bits_properties,
-                             const FormatSection &to_conv) {
+                             const FormatSection<CharT> &to_conv) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   static constexpr uint32_t MAX_POSSIBLE_FRACTION_LEN =
       fputil::FPBits<float128>::FRACTION_LEN;
@@ -90,15 +95,16 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
       fputil::FPBits<double>::EXP_LEN;
 #endif
 
-  char sign_char = 0;
+  CharT sign_char = 0;
 
   if (fp_bits_properties.is_negative)
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over
+                            // SPACE_PREFIX
   else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
            FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   constexpr size_t BITS_IN_HEX_DIGIT = 4;
 
@@ -118,7 +124,7 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
   // since the size must be constant.
   constexpr size_t MANT_BUFF_LEN =
       (MAX_POSSIBLE_FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
-  char mant_buffer[MANT_BUFF_LEN];
+  CharT mant_buffer[MANT_BUFF_LEN];
 
   size_t mant_len = (fp_bits_properties.fraction_bits / BITS_IN_HEX_DIGIT) + 1;
 
@@ -181,13 +187,24 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
 
   size_t mant_cur = mant_len;
   size_t first_non_zero = 1;
+  bool is_upper = internal::isupper(to_conv.conv_name);
   for (; mant_cur > 0; --mant_cur, fp_bits_properties.mantissa >>= 4) {
-    char mant_mod_16 = static_cast<char>(fp_bits_properties.mantissa % 16);
-    char new_digit = internal::int_to_b36_char(mant_mod_16);
-    if (internal::isupper(to_conv.conv_name))
+    int mant_mod_16 = static_cast<int>(fp_bits_properties.mantissa % 16);
+    CharT new_digit;
+    if constexpr (cpp::is_same_v<CharT, char>) {
+      new_digit = internal::int_to_b36_char(mant_mod_16);
+    } else {
+#if defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+      static_assert(false, "wchar_t not supported.");
+#else  // LIBC_COPT_PRINTF_DISABLE_WIDE
+      static_assert(cpp::is_same_v<CharT, wchar_t>);
+      new_digit = internal::int_to_b36_wchar(mant_mod_16);
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+    }
+    if (is_upper)
       new_digit = internal::toupper(new_digit);
     mant_buffer[mant_cur - 1] = new_digit;
-    if (new_digit != '0' && first_non_zero < mant_cur)
+    if (new_digit != CharT{'0'} && first_non_zero < mant_cur)
       first_non_zero = mant_cur;
   }
 
@@ -203,7 +220,7 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
   // 11 -> 4
   // 8  -> 3
   constexpr size_t EXP_LEN = (((MAX_POSSIBLE_EXP_LEN * 5) + 15) / 16) + 1;
-  char exp_buffer[EXP_LEN];
+  CharT exp_buffer[EXP_LEN];
 
   bool exp_is_negative = false;
   if (fp_bits_properties.exponent < 0) {
@@ -214,15 +231,25 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
   size_t exp_cur = EXP_LEN;
   for (; fp_bits_properties.exponent > 0;
        --exp_cur, fp_bits_properties.exponent /= 10) {
-    exp_buffer[exp_cur - 1] =
-        internal::int_to_b36_char(fp_bits_properties.exponent % 10);
+    if constexpr (cpp::is_same_v<CharT, char>) {
+      exp_buffer[exp_cur - 1] =
+          internal::int_to_b36_char(fp_bits_properties.exponent % 10);
+    } else {
+#if defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+      static_assert(false, "wchar_t not supported.");
+#else  // LIBC_COPT_PRINTF_DISABLE_WIDE
+      static_assert(cpp::is_same_v<CharT, wchar_t>);
+      exp_buffer[exp_cur - 1] =
+          internal::int_to_b36_wchar(fp_bits_properties.exponent % 10);
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+    }
   }
   if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0.
-    exp_buffer[EXP_LEN - 1] = '0';
+    exp_buffer[EXP_LEN - 1] = CharT{'0'};
     exp_cur = EXP_LEN - 1;
   }
 
-  exp_buffer[exp_cur - 1] = exp_is_negative ? '-' : '+';
+  exp_buffer[exp_cur - 1] = exp_is_negative ? CharT{'-'} : CharT{'+'};
   --exp_cur;
 
   // these are signed to prevent underflow due to negative values. The eventual
@@ -232,10 +259,10 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
 
   // prefix is "0x", and always appears.
   constexpr size_t PREFIX_LEN = 2;
-  char prefix[PREFIX_LEN];
-  prefix[0] = '0';
-  prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
-  const cpp::string_view prefix_str(prefix, PREFIX_LEN);
+  CharT prefix[PREFIX_LEN];
+  prefix[0] = CharT{'0'};
+  prefix[1] = is_upper ? CharT{'X'} : CharT{'x'};
+  const cpp::basic_string_view<CharT> prefix_str(prefix, PREFIX_LEN);
 
   // If the precision is greater than the actual result, pad with 0s
   if (to_conv.precision > static_cast<int>(mant_digits - 1))
@@ -244,10 +271,11 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
   bool has_hexadecimal_point =
       (mant_digits > 1) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
                             FormatFlags::ALTERNATE_FORM);
-  constexpr cpp::string_view HEXADECIMAL_POINT(".");
+
+  constexpr CharT HEXADECIMAL_POINT = CharT{'.'};
 
   // This is for the letter 'p' before the exponent.
-  const char exp_separator = internal::islower(to_conv.conv_name) ? 'p' : 'P';
+  const CharT exp_separator = is_upper ? CharT{'P'} : CharT{'p'};
   constexpr int EXP_SEPARATOR_LEN = 1;
 
   padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
@@ -270,31 +298,31 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
     if (mant_digits > 1)
       RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1}));
     if (trailing_zeroes > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, trailing_zeroes));
     RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
     RET_IF_RESULT_NEGATIVE(
         writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
     if (padding > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding));
   } else {
     // The pattern is (spaces), (sign), 0x, (zeroes), digit, (.), (other
     // digits), (zeroes), p, exponent
     if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) !=
                           FormatFlags::LEADING_ZEROES))
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding));
     if (sign_char > 0)
       RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
     RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
     if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) ==
                           FormatFlags::LEADING_ZEROES))
-      RET_IF_RESULT_NEGATIVE(writer->write('0', padding));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, padding));
     RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
     if (has_hexadecimal_point)
       RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
     if (mant_digits > 1)
       RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1}));
     if (trailing_zeroes > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, trailing_zeroes));
     RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
     RET_IF_RESULT_NEGATIVE(
         writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
@@ -302,11 +330,12 @@ convert_finite_float_hex_exp(Writer<mode> *writer,
   return WRITE_OK;
 }
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_float_hex_exp(Writer<mode> *writer,
-                                      const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_float_hex_exp(Writer<mode, CharT> *writer,
+                                      const FormatSection<CharT> &to_conv) {
   FloatHexExpFPBitsProperties fp_bits_properties =
-      get_float_hex_exp_fp_bits_properties_lm(to_conv);
+      get_float_hex_exp_fp_bits_properties_lm(to_conv.length_modifier,
+                                              to_conv.conv_val_raw);
   if (fp_bits_properties.is_inf_or_nan)
     return convert_inf_nan(
         writer,

diff  --git a/libc/src/__support/printf_core/float_inf_nan_converter.h b/libc/src/__support/printf_core/float_inf_nan_converter.h
index 5d306a13d5f137..be16d515e4725b 100644
--- a/libc/src/__support/printf_core/float_inf_nan_converter.h
+++ b/libc/src/__support/printf_core/float_inf_nan_converter.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_FLOAT_INF_NAN_CONVERTER_H
 #define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_FLOAT_INF_NAN_CONVERTER_H
 
+#include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/ctype_utils.h"
 #include "src/__support/macros/config.h"
@@ -16,6 +17,10 @@
 #include "src/__support/printf_core/core_structs.h"
 #include "src/__support/printf_core/writer.h"
 
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+#include "src/__support/wctype_utils.h"
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include <inttypes.h>
 #include <stddef.h>
 
@@ -27,21 +32,22 @@ struct InfNanFPBitsProperties {
   bool mantissa_is_zero;
 };
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_inf_nan(Writer<mode> *writer,
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_inf_nan(Writer<mode, CharT> *writer,
                                 InfNanFPBitsProperties fp_bits_properties,
-                                const FormatSection &to_conv) {
+                                const FormatSection<CharT> &to_conv) {
   // All of the letters will be defined relative to variable a, which will be
   // the appropriate case based on the case of the conversion.
-  char sign_char = 0;
+  CharT sign_char = 0;
 
   if (fp_bits_properties.is_negative)
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over
+                            // SPACE_PREFIX
   else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
            FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   // Both "inf" and "nan" are the same number of characters, being 3.
   int padding = to_conv.min_width - (sign_char > 0 ? 1 : 0) - 3;
@@ -51,21 +57,41 @@ LIBC_INLINE int convert_inf_nan(Writer<mode> *writer,
 
   if (padding > 0 && ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) !=
                       FormatFlags::LEFT_JUSTIFIED))
-    RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+    RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding));
 
   if (sign_char)
     RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
   if (fp_bits_properties.mantissa_is_zero) { // inf
-    RET_IF_RESULT_NEGATIVE(
-        writer->write(internal::islower(to_conv.conv_name) ? "inf" : "INF"));
+    if constexpr (cpp::is_same_v<CharT, char>) {
+      RET_IF_RESULT_NEGATIVE(
+          writer->write(internal::islower(to_conv.conv_name) ? "inf" : "INF"));
+    } else {
+#if defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+      static_assert(false, "wchar_t not supported.");
+#else  // LIBC_COPT_PRINTF_DISABLE_WIDE
+      static_assert(cpp::is_same_v<CharT, wchar_t>);
+      RET_IF_RESULT_NEGATIVE(writer->write(
+          internal::islower(to_conv.conv_name) ? L"inf" : L"INF"));
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+    }
   } else { // nan
-    RET_IF_RESULT_NEGATIVE(
-        writer->write(internal::islower(to_conv.conv_name) ? "nan" : "NAN"));
+    if constexpr (cpp::is_same_v<CharT, char>) {
+      RET_IF_RESULT_NEGATIVE(
+          writer->write(internal::islower(to_conv.conv_name) ? "nan" : "NAN"));
+    } else {
+#if defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+      static_assert(false, "wchar_t not supported.");
+#else  // LIBC_COPT_PRINTF_DISABLE_WIDE
+      static_assert(cpp::is_same_v<CharT, wchar_t>);
+      RET_IF_RESULT_NEGATIVE(writer->write(
+          internal::islower(to_conv.conv_name) ? L"nan" : L"NAN"));
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+    }
   }
 
   if (padding > 0 && ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
                       FormatFlags::LEFT_JUSTIFIED))
-    RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+    RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, padding));
 
   return WRITE_OK;
 }

diff  --git a/libc/src/__support/printf_core/int_converter.h b/libc/src/__support/printf_core/int_converter.h
index 786d9ff2268ff0..97c29e0da3c1af 100644
--- a/libc/src/__support/printf_core/int_converter.h
+++ b/libc/src/__support/printf_core/int_converter.h
@@ -26,16 +26,27 @@ namespace printf_core {
 
 namespace details {
 
-using HexFmt = IntegerToString<uintmax_t, radix::Hex>;
-using HexFmtUppercase = IntegerToString<uintmax_t, radix::Hex::Uppercase>;
-using OctFmt = IntegerToString<uintmax_t, radix::Oct>;
-using DecFmt = IntegerToString<uintmax_t>;
-using BinFmt = IntegerToString<uintmax_t, radix::Bin>;
+template <typename CharT>
+using HexFmt = IntegerToString<uintmax_t, radix::Hex, CharT>;
 
-LIBC_INLINE constexpr size_t num_buf_size() {
+template <typename CharT>
+using HexFmtUppercase =
+    IntegerToString<uintmax_t, radix::Hex::Uppercase, CharT>;
+
+template <typename CharT>
+using OctFmt = IntegerToString<uintmax_t, radix::Oct, CharT>;
+
+template <typename CharT>
+using DecFmt = IntegerToString<uintmax_t, radix::Dec, CharT>;
+
+template <typename CharT>
+using BinFmt = IntegerToString<uintmax_t, radix::Bin, CharT>;
+
+template <typename CharT> LIBC_INLINE constexpr size_t num_buf_size() {
   cpp::array<size_t, 5> sizes{
-      HexFmt::buffer_size(), HexFmtUppercase::buffer_size(),
-      OctFmt::buffer_size(), DecFmt::buffer_size(), BinFmt::buffer_size()};
+      HexFmt<CharT>::buffer_size(), HexFmtUppercase<CharT>::buffer_size(),
+      OctFmt<CharT>::buffer_size(), DecFmt<CharT>::buffer_size(),
+      BinFmt<CharT>::buffer_size()};
 
   auto result = sizes[0];
   for (size_t i = 1; i < sizes.size(); i++)
@@ -43,27 +54,29 @@ LIBC_INLINE constexpr size_t num_buf_size() {
   return result;
 }
 
-LIBC_INLINE cpp::optional<cpp::string_view>
-num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
-  if (internal::tolower(conv_name) == 'x') {
-    if (internal::islower(conv_name))
-      return HexFmt::format_to(bufref, num);
-    else
-      return HexFmtUppercase::format_to(bufref, num);
-  } else if (conv_name == 'o') {
-    return OctFmt::format_to(bufref, num);
-  } else if (internal::tolower(conv_name) == 'b') {
-    return BinFmt::format_to(bufref, num);
-  } else {
-    return DecFmt::format_to(bufref, num);
+template <typename CharT>
+LIBC_INLINE cpp::optional<cpp::basic_string_view<CharT>>
+num_to_strview(uintmax_t num, cpp::span<CharT> bufref, CharT conv_name) {
+  switch (conv_name) {
+  case CharT{'x'}:
+    return HexFmt<CharT>::format_to(bufref, num);
+  case CharT{'X'}:
+    return HexFmtUppercase<CharT>::format_to(bufref, num);
+  case CharT{'o'}:
+    return OctFmt<CharT>::format_to(bufref, num);
+  case CharT('b'):
+  case CharT('B'):
+    return BinFmt<CharT>::format_to(bufref, num);
+  default:
+    return DecFmt<CharT>::format_to(bufref, num);
   }
 }
 
 } // namespace details
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_int(Writer<mode> *writer,
-                            const FormatSection &to_conv) {
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_int(Writer<mode, CharT> *writer,
+                            const FormatSection<CharT> &to_conv) {
   static constexpr size_t BITS_IN_BYTE = 8;
   static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;
 
@@ -72,7 +85,7 @@ LIBC_INLINE int convert_int(Writer<mode> *writer,
   FormatFlags flags = to_conv.flags;
 
   // If the conversion is signed, then handle negative values.
-  if (to_conv.conv_name == 'd' || to_conv.conv_name == 'i') {
+  if (to_conv.conv_name == CharT{'d'} || to_conv.conv_name == CharT{'i'}) {
     // Check if the number is negative by checking the high bit. This works even
     // for smaller numbers because they're sign extended by default.
     if ((num & (uintmax_t(1) << (BITS_IN_NUM - 1))) > 0) {
@@ -88,41 +101,38 @@ LIBC_INLINE int convert_int(Writer<mode> *writer,
 
   num =
       apply_length_modifier(num, {to_conv.length_modifier, to_conv.bit_width});
-  cpp::array<char, details::num_buf_size()> buf;
-  auto str = details::num_to_strview(num, buf, to_conv.conv_name);
+  cpp::array<CharT, details::num_buf_size<CharT>()> buf;
+  auto str = details::num_to_strview<CharT>(num, buf, to_conv.conv_name);
   if (!str)
     return INT_CONVERSION_ERROR;
 
   size_t digits_written = str->size();
 
-  char sign_char = 0;
+  CharT sign_char = 0;
 
   if (is_negative)
-    sign_char = '-';
+    sign_char = CharT{'-'};
   else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
-    sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+    sign_char = CharT{'+'}; // FORCE_SIGN has precedence over SPACE_PREFIX
   else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX)
-    sign_char = ' ';
+    sign_char = CharT{' '};
 
   // These are signed to prevent underflow due to negative values. The eventual
   // values will always be non-negative.
   int zeroes;
   int spaces;
 
-  // prefix is "0x" for hexadecimal, or the sign character for signed
-  // conversions. Since hexadecimal is unsigned these will never conflict.
+  // Prefix is "0x" or "OX" for hexadecimal, "0b" or "0B" for binary, or the
+  // sign character for signed conversions. Since hexadecimal and binary are
+  // unsigned these will never conflict.
   size_t prefix_len;
-  char prefix[2];
-  if ((internal::tolower(to_conv.conv_name) == 'x') &&
-      ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
-    prefix_len = 2;
-    prefix[0] = '0';
-    prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
-  } else if ((internal::tolower(to_conv.conv_name) == 'b') &&
-             ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
+  CharT prefix[2];
+  if ((to_conv.conv_name == CharT{'x'} || to_conv.conv_name == CharT{'X'} ||
+       to_conv.conv_name == CharT{'b'} || to_conv.conv_name == CharT{'B'}) &&
+      (flags & FormatFlags::ALTERNATE_FORM) != 0 && num != 0) {
     prefix_len = 2;
-    prefix[0] = '0';
-    prefix[1] = internal::islower(to_conv.conv_name) ? 'b' : 'B';
+    prefix[0] = CharT{'0'};
+    prefix[1] = to_conv.conv_name;
   } else {
     prefix_len = (sign_char == 0 ? 0 : 1);
     prefix[0] = sign_char;
@@ -175,7 +185,7 @@ LIBC_INLINE int convert_int(Writer<mode> *writer,
   //    2b) ... because it is just "0", unless it will not write any digits.
   const bool has_leading_zero =
       (zeroes > 0) || ((num == 0) && (digits_written != 0));
-  if ((to_conv.conv_name == 'o') &&
+  if ((to_conv.conv_name == CharT{'o'}) &&
       ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) &&
       !has_leading_zero) {
     zeroes = 1;
@@ -187,19 +197,19 @@ LIBC_INLINE int convert_int(Writer<mode> *writer,
     if (prefix_len != 0)
       RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len}));
     if (zeroes > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, zeroes));
     if (digits_written > 0)
       RET_IF_RESULT_NEGATIVE(writer->write(*str));
     if (spaces > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, spaces));
   } else {
     // Else it goes spaces prefix zeroes digits
     if (spaces > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write(' ', spaces));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{' '}, spaces));
     if (prefix_len != 0)
       RET_IF_RESULT_NEGATIVE(writer->write({prefix, prefix_len}));
     if (zeroes > 0)
-      RET_IF_RESULT_NEGATIVE(writer->write('0', zeroes));
+      RET_IF_RESULT_NEGATIVE(writer->write(CharT{'0'}, zeroes));
     if (digits_written > 0)
       RET_IF_RESULT_NEGATIVE(writer->write(*str));
   }

diff  --git a/libc/src/__support/printf_core/parser.h b/libc/src/__support/printf_core/parser.h
index a04f481f79e656..e0129a98fb891a 100644
--- a/libc/src/__support/printf_core/parser.h
+++ b/libc/src/__support/printf_core/parser.h
@@ -116,8 +116,8 @@ template <typename ArgProvider, typename CharT> class Parser {
   // specified format section. This can either be a raw format section with no
   // conversion, or a format section with a conversion that has all of its
   // variables stored in the format section.
-  LIBC_INLINE BasicFormatSection<CharT> get_next_section() {
-    BasicFormatSection<CharT> section;
+  LIBC_INLINE FormatSection<CharT> get_next_section() {
+    FormatSection<CharT> section;
     size_t starting_pos = cur_pos;
     if (str[cur_pos] == CharT{'%'}) {
       // format section

diff  --git a/libc/src/__support/printf_core/printf_main.h b/libc/src/__support/printf_core/printf_main.h
index 401adb0735dd3b..58eac27c4c4bcb 100644
--- a/libc/src/__support/printf_core/printf_main.h
+++ b/libc/src/__support/printf_core/printf_main.h
@@ -28,7 +28,7 @@ ErrorOr<size_t> printf_main_modular(Writer<mode> *writer,
                                     internal::ArgList &args) {
   Parser<internal::ArgList, char> parser(str, args);
   int result = 0;
-  for (FormatSection cur_section = parser.get_next_section();
+  for (FormatSection<char> cur_section = parser.get_next_section();
        !cur_section.raw_string.empty();
        cur_section = parser.get_next_section()) {
     if (cur_section.has_conv)

diff  --git a/libc/src/__support/printf_core/ptr_converter.h b/libc/src/__support/printf_core/ptr_converter.h
index 6415439bbb2a5b..8c0d49589d290c 100644
--- a/libc/src/__support/printf_core/ptr_converter.h
+++ b/libc/src/__support/printf_core/ptr_converter.h
@@ -18,23 +18,21 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_pointer(Writer<mode> *writer,
-                                const FormatSection &to_conv) {
-  FormatSection new_conv = to_conv;
-
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_pointer(Writer<mode, CharT> *writer,
+                                FormatSection<CharT> to_conv) {
   if (to_conv.conv_val_ptr == nullptr) {
     constexpr char NULLPTR_STR[] = "(nullptr)";
-    new_conv.conv_name = 's';
-    new_conv.conv_val_ptr = const_cast<char *>(NULLPTR_STR);
-    return convert_string(writer, new_conv);
+    to_conv.conv_name = 's';
+    to_conv.conv_val_ptr = const_cast<char *>(NULLPTR_STR);
+    return convert_string(writer, to_conv);
   }
-  new_conv.conv_name = 'x';
-  new_conv.flags =
+  to_conv.conv_name = 'x';
+  to_conv.flags =
       static_cast<FormatFlags>(to_conv.flags | FormatFlags::ALTERNATE_FORM);
-  new_conv.length_modifier = LengthModifier::t;
-  new_conv.conv_val_raw = reinterpret_cast<uintptr_t>(to_conv.conv_val_ptr);
-  return convert_int(writer, new_conv);
+  to_conv.length_modifier = LengthModifier::t;
+  to_conv.conv_val_raw = reinterpret_cast<uintptr_t>(to_conv.conv_val_ptr);
+  return convert_int(writer, to_conv);
 }
 
 } // namespace printf_core

diff  --git a/libc/src/__support/printf_core/strerror_converter.h b/libc/src/__support/printf_core/strerror_converter.h
index 4e4e61295b533a..bda1b022a1ca44 100644
--- a/libc/src/__support/printf_core/strerror_converter.h
+++ b/libc/src/__support/printf_core/strerror_converter.h
@@ -19,10 +19,9 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_strerror(Writer<mode> *writer,
-                                 const FormatSection &to_conv) {
-  FormatSection new_conv = to_conv;
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_strerror(Writer<mode, CharT> *writer,
+                                 FormatSection<CharT> to_conv) {
   const int error_num = static_cast<int>(to_conv.conv_val_raw);
 
   // The %m conversion takes no arguments passes the result of strerror(errno)
@@ -37,10 +36,10 @@ LIBC_INLINE int convert_strerror(Writer<mode> *writer,
   if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) == 0) {
     char strerror_buff[64];
     auto strerror_result = get_error_string(error_num, strerror_buff);
-    new_conv.conv_val_ptr =
+    to_conv.conv_val_ptr =
         reinterpret_cast<void *>(const_cast<char *>(strerror_result.data()));
-    new_conv.conv_name = 's';
-    return convert_string(writer, new_conv);
+    to_conv.conv_name = CharT{'s'};
+    return convert_string(writer, to_conv);
   } else {
     // alt form
 
@@ -58,14 +57,14 @@ LIBC_INLINE int convert_strerror(Writer<mode> *writer,
     auto errno_name = try_get_errno_name(error_num);
     // if there's a name available, use it.
     if (errno_name) {
-      new_conv.conv_val_ptr =
+      to_conv.conv_val_ptr =
           reinterpret_cast<void *>(const_cast<char *>(errno_name->data()));
-      new_conv.conv_name = 's';
-      return convert_string(writer, new_conv);
+      to_conv.conv_name = CharT{'s'};
+      return convert_string(writer, to_conv);
     } else {
       // else do an int conversion
-      new_conv.conv_name = 'd';
-      return convert_int(writer, new_conv);
+      to_conv.conv_name = CharT{'d'};
+      return convert_int(writer, to_conv);
     }
   }
 }

diff  --git a/libc/src/__support/printf_core/string_converter.h b/libc/src/__support/printf_core/string_converter.h
index 5352bd8306ba6c..4a72ee03d43ada 100644
--- a/libc/src/__support/printf_core/string_converter.h
+++ b/libc/src/__support/printf_core/string_converter.h
@@ -16,6 +16,7 @@
 #include "src/__support/wchar/string_converter.h"
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
+#include "src/__support/CPP/type_traits.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/printf_core/converter_utils.h"
 #include "src/__support/printf_core/core_structs.h"
@@ -28,8 +29,8 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 template <OverflowMode mode>
-LIBC_INLINE int char_writer(Writer<mode> *writer,
-                            const FormatSection &to_conv) {
+LIBC_INLINE int write_char_string_as_chars(Writer<mode, char> *writer,
+                                           const FormatSection<char> &to_conv) {
   const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);
   size_t string_len = 0;
 
@@ -67,8 +68,9 @@ LIBC_INLINE int char_writer(Writer<mode> *writer,
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
 template <OverflowMode mode>
-LIBC_INLINE int wchar_writer(Writer<mode> *writer,
-                             const FormatSection &to_conv) {
+LIBC_INLINE int
+write_wchar_string_as_chars(Writer<mode, char> *writer,
+                            const FormatSection<char> &to_conv) {
   size_t string_len = 0;
   const char32_t *wstr_ptr =
       reinterpret_cast<const char32_t *>(to_conv.conv_val_ptr);
@@ -119,24 +121,121 @@ LIBC_INLINE int wchar_writer(Writer<mode> *writer,
 
   return WRITE_OK;
 }
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
 template <OverflowMode mode>
-LIBC_INLINE int convert_string(Writer<mode> *writer,
-                               const FormatSection &to_conv) {
-  int ret = 0;
-  if (to_conv.length_modifier == LengthModifier::l) {
-    // find length and print wide char characters
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-    ret = wchar_writer(writer, to_conv);
-#else
-    ret = char_writer(writer, to_conv);
-#endif
-  } else {
-    ret = char_writer(writer, to_conv);
+LIBC_INLINE int
+write_char_string_as_wchars(Writer<mode, wchar_t> *writer,
+                            const FormatSection<wchar_t> &to_conv) {
+  const char8_t *str_ptr =
+      reinterpret_cast<const char8_t *>(to_conv.conv_val_ptr);
+
+#if !defined(LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS)
+  if (str_ptr == nullptr)
+    str_ptr = reinterpret_cast<const char8_t *>(u8"(null)");
+#endif // !LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+
+  size_t precision =
+      to_conv.precision < 0 ? SIZE_MAX : static_cast<size_t>(to_conv.precision);
+
+  size_t converted_string_len = 0;
+  {
+    internal::mbstate mbstate;
+    internal::StringConverter<char8_t> length_counter(str_ptr, &mbstate,
+                                                      precision);
+    for (auto converted = length_counter.pop<wchar_t>();
+         converted.has_value() && converted.value() != L'\0';
+         converted = length_counter.pop<wchar_t>()) {
+      ++converted_string_len;
+    }
+  }
+
+  size_t padding_spaces =
+      to_conv.min_width > static_cast<int>(converted_string_len)
+          ? to_conv.min_width - converted_string_len
+          : 0;
+
+  // If the padding is on the left side, write the spaces first.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(L' ', padding_spaces));
   }
 
-  return ret;
+  {
+    internal::mbstate mbstate;
+    internal::StringConverter<char8_t> out_conv(str_ptr, &mbstate, precision);
+    for (size_t i = 0; i < converted_string_len; ++i) {
+      RET_IF_RESULT_NEGATIVE(writer->write(*out_conv.pop<wchar_t>()));
+    }
+  }
+
+  // If the padding is on the right side, write the spaces last.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) != 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(L' ', padding_spaces));
+  }
+  return WRITE_OK;
+}
+
+template <OverflowMode mode>
+LIBC_INLINE int
+write_wchar_string_as_wchars(Writer<mode, wchar_t> *writer,
+                             const FormatSection<wchar_t> &to_conv) {
+  const wchar_t *wstr_ptr =
+      reinterpret_cast<const wchar_t *>(to_conv.conv_val_ptr);
+  size_t precision =
+      to_conv.precision < 0 ? SIZE_MAX : static_cast<size_t>(to_conv.precision);
+
+#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+  if (wstr_ptr == nullptr)
+    wstr_ptr = L"(null)";
+#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+
+  size_t string_len = 0;
+  while (string_len < precision && wstr_ptr[string_len] != L'\0') {
+    ++string_len;
+  }
+
+  size_t padding_spaces = to_conv.min_width > static_cast<int>(string_len)
+                              ? to_conv.min_width - string_len
+                              : 0;
+
+  // If the padding is on the left side, write the spaces first.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(L' ', padding_spaces));
+  }
+
+  RET_IF_RESULT_NEGATIVE(writer->write({wstr_ptr, string_len}));
+
+  // If the padding is on the right side, write the spaces last.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) != 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(L' ', padding_spaces));
+  }
+
+  return WRITE_OK;
+}
+
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE int convert_string(Writer<mode, CharT> *writer,
+                               const FormatSection<CharT> &to_conv) {
+#if defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+  static_assert(cpp::is_same_v<CharT, char>, "wchar_t not supported.");
+  return write_char_string_as_chars(writer, to_conv);
+#else  // LIBC_COPT_PRINTF_DISABLE_WIDE
+  if constexpr (cpp::is_same_v<CharT, char>) {
+    if (to_conv.length_modifier == LengthModifier::l)
+      return write_wchar_string_as_chars(writer, to_conv);
+    return write_char_string_as_chars(writer, to_conv);
+  } else {
+    static_assert(cpp::is_same_v<CharT, wchar_t>);
+    if (to_conv.length_modifier == LengthModifier::l)
+      return write_wchar_string_as_wchars(writer, to_conv);
+    return write_char_string_as_wchars(writer, to_conv);
+  }
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 }
 
 } // namespace printf_core

diff  --git a/libc/src/__support/printf_core/write_int_converter.h b/libc/src/__support/printf_core/write_int_converter.h
index 882963c1c44e27..5353d0db12298f 100644
--- a/libc/src/__support/printf_core/write_int_converter.h
+++ b/libc/src/__support/printf_core/write_int_converter.h
@@ -9,9 +9,9 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_WRITE_INT_CONVERTER_H
 #define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_WRITE_INT_CONVERTER_H
 
+#include "hdr/types/size_t.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/printf_core/core_structs.h"
-#include "src/__support/printf_core/writer.h"
 
 #include <inttypes.h>
 #include <stddef.h>
@@ -19,49 +19,46 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <OverflowMode mode>
-LIBC_INLINE int convert_write_int(Writer<mode> *writer,
-                                  const FormatSection &to_conv) {
+LIBC_INLINE int convert_write_int(size_t written,
+                                  LengthModifier conv_length_modifier,
+                                  void *conv_val_ptr) {
 
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
   // This is an additional check added by LLVM-libc.
-  if (to_conv.conv_val_ptr == nullptr)
+  if (conv_val_ptr == nullptr)
     return NULLPTR_WRITE_ERROR;
 #endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 
-  size_t written = writer->get_chars_written();
-
-  switch (to_conv.length_modifier) {
+  switch (conv_length_modifier) {
   case LengthModifier::none:
-    *reinterpret_cast<int *>(to_conv.conv_val_ptr) = static_cast<int>(written);
+    *reinterpret_cast<int *>(conv_val_ptr) = static_cast<int>(written);
     break;
   case LengthModifier::l:
-    *reinterpret_cast<long *>(to_conv.conv_val_ptr) = written;
+    *reinterpret_cast<long *>(conv_val_ptr) = written;
     break;
   case LengthModifier::ll:
   case LengthModifier::L:
-    *reinterpret_cast<long long *>(to_conv.conv_val_ptr) = written;
+    *reinterpret_cast<long long *>(conv_val_ptr) = written;
     break;
   case LengthModifier::h:
-    *reinterpret_cast<short *>(to_conv.conv_val_ptr) =
-        static_cast<short>(written);
+    *reinterpret_cast<short *>(conv_val_ptr) = static_cast<short>(written);
     break;
   case LengthModifier::hh:
-    *reinterpret_cast<signed char *>(to_conv.conv_val_ptr) =
+    *reinterpret_cast<signed char *>(conv_val_ptr) =
         static_cast<signed char>(written);
     break;
   case LengthModifier::z:
-    *reinterpret_cast<size_t *>(to_conv.conv_val_ptr) = written;
+    *reinterpret_cast<size_t *>(conv_val_ptr) = written;
     break;
   case LengthModifier::t:
-    *reinterpret_cast<ptr
diff _t *>(to_conv.conv_val_ptr) = written;
+    *reinterpret_cast<ptr
diff _t *>(conv_val_ptr) = written;
     break;
   case LengthModifier::j:
 #ifndef LIBC_COPT_PRINTF_DISABLE_BITINT
   case LengthModifier::w:
   case LengthModifier::wf:
 #endif // LIBC_COPT_PRINTF_DISABLE_BITINT
-    *reinterpret_cast<uintmax_t *>(to_conv.conv_val_ptr) = written;
+    *reinterpret_cast<uintmax_t *>(conv_val_ptr) = written;
     break;
 #if defined(LIBC_TYPES_HAS_NATIVE_FLOAT128)
   case (LengthModifier::Q): // 'Q' is not valid for integer format; this case

diff  --git a/libc/src/stdlib/str_from_util.h b/libc/src/stdlib/str_from_util.h
index 87d77100d9c98f..746fe7a7b43e85 100644
--- a/libc/src/stdlib/str_from_util.h
+++ b/libc/src/stdlib/str_from_util.h
@@ -34,7 +34,7 @@ namespace internal {
 template <typename T, printf_core::OverflowMode overflow_mode>
 LIBC_INLINE int strfromfloat_convert(printf_core::Writer<overflow_mode> *writer,
                                      const char *__restrict format, T fp) {
-  printf_core::FormatSection section = {};
+  printf_core::FormatSection<char> section = {};
   size_t cur_pos = 0;
 
   if (format[cur_pos] == '%') {

diff  --git a/libc/test/UnitTest/PrintfMatcher.cpp b/libc/test/UnitTest/PrintfMatcher.cpp
index a8e4850d0d0f74..2fe3482ea54383 100644
--- a/libc/test/UnitTest/PrintfMatcher.cpp
+++ b/libc/test/UnitTest/PrintfMatcher.cpp
@@ -21,8 +21,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace testing {
 
-using printf_core::BasicFormatSection;
 using printf_core::FormatFlags;
+using printf_core::FormatSection;
 using printf_core::LengthModifier;
 
 namespace {
@@ -41,7 +41,7 @@ namespace {
     tlog << #lm << "\n\tbit width: :" << bw;                                   \
     break
 
-template <typename CharT> void display_impl(BasicFormatSection<CharT> form) {
+template <typename CharT> void display_impl(FormatSection<CharT> form) {
   tlog << "Raw String (len " << form.raw_string.size() << "): \"";
   cpp::string raw_string_utf8;
   if constexpr (cpp::is_same_v<CharT, char>) {
@@ -104,11 +104,11 @@ template <typename CharT> void display_impl(BasicFormatSection<CharT> form) {
 
 } // anonymous namespace
 
-void display(const BasicFormatSection<char> &format_section) {
+void display(const FormatSection<char> &format_section) {
   display_impl(format_section);
 }
 
-void display(const BasicFormatSection<wchar_t> &format_section) {
+void display(const FormatSection<wchar_t> &format_section) {
   display_impl(format_section);
 }
 

diff  --git a/libc/test/UnitTest/PrintfMatcher.h b/libc/test/UnitTest/PrintfMatcher.h
index a2280c6c7e45d1..656856e25b708f 100644
--- a/libc/test/UnitTest/PrintfMatcher.h
+++ b/libc/test/UnitTest/PrintfMatcher.h
@@ -16,20 +16,19 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace testing {
 
-void display(const printf_core::BasicFormatSection<char> &format_section);
-void display(const printf_core::BasicFormatSection<wchar_t> &format_section);
+void display(const printf_core::FormatSection<char> &format_section);
+void display(const printf_core::FormatSection<wchar_t> &format_section);
 
 template <typename CharT>
-class FormatSectionMatcher
-    : public Matcher<printf_core::BasicFormatSection<CharT>> {
-  printf_core::BasicFormatSection<CharT> expected;
-  printf_core::BasicFormatSection<CharT> actual;
+class FormatSectionMatcher : public Matcher<printf_core::FormatSection<CharT>> {
+  printf_core::FormatSection<CharT> expected;
+  printf_core::FormatSection<CharT> actual;
 
 public:
-  FormatSectionMatcher(printf_core::BasicFormatSection<CharT> expectedValue)
+  FormatSectionMatcher(printf_core::FormatSection<CharT> expectedValue)
       : expected(expectedValue) {}
 
-  bool match(printf_core::BasicFormatSection<CharT> actualValue) {
+  bool match(printf_core::FormatSection<CharT> actualValue) {
     actual = actualValue;
     return expected == actual;
   }
@@ -46,7 +45,7 @@ class FormatSectionMatcher
 
 template <typename CharT>
 FormatSectionMatcher<CharT>
-MakeFormatSectionMatcher(printf_core::BasicFormatSection<CharT> expected) {
+MakeFormatSectionMatcher(printf_core::FormatSection<CharT> expected) {
   return FormatSectionMatcher<CharT>(expected);
 }
 

diff  --git a/libc/test/src/__support/printf_core/converter_test.cpp b/libc/test/src/__support/printf_core/converter_test.cpp
index a09873c128eabc..facbda405acb7d 100644
--- a/libc/test/src/__support/printf_core/converter_test.cpp
+++ b/libc/test/src/__support/printf_core/converter_test.cpp
@@ -10,196 +10,460 @@
 #include "src/__support/printf_core/core_structs.h"
 #include "src/__support/printf_core/writer.h"
 
+#include "test/UnitTest/CharLiteralUtils.h"
 #include "test/UnitTest/Test.h"
 
-class LlvmLibcPrintfConverterTest : public LIBC_NAMESPACE::testing::Test {
-protected:
-  LlvmLibcPrintfConverterTest()
-      : writer(LIBC_NAMESPACE::printf_core::make_drop_overflow_writer(
-            str, sizeof(str) - 1)),
-        wb(writer.get_write_buffer()) {}
-
-  char str[60];
-  LIBC_NAMESPACE::printf_core::Writer<LIBC_NAMESPACE::printf_core::Mode<
-      LIBC_NAMESPACE::printf_core::OverflowMode::DROP_OVERFLOW>::value>
-      writer;
-  LIBC_NAMESPACE::printf_core::WriteBuffer<char> &wb;
-};
-
-TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {
-  LIBC_NAMESPACE::printf_core::FormatSection raw_section;
+namespace {
+
+using LIBC_NAMESPACE::printf_core::FormatSection;
+using LIBC_NAMESPACE::printf_core::make_drop_overflow_writer;
+using LIBC_NAMESPACE::printf_core::Mode;
+using LIBC_NAMESPACE::printf_core::OverflowMode;
+using LIBC_NAMESPACE::printf_core::WriteBuffer;
+using LIBC_NAMESPACE::printf_core::Writer;
+
+#if defined(LIBC_TYPES_WCHAR_T_IS_UTF32) &&                                    \
+    !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+using TestCharTypes = LIBC_NAMESPACE::testing::TypeList<char, wchar_t>;
+#else
+using TestCharTypes = LIBC_NAMESPACE::testing::TypeList<char>;
+#endif
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, SimpleRawConversion, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+
+  FormatSection<CharT> raw_section;
   raw_section.has_conv = false;
-  raw_section.raw_string = "abc";
+  raw_section.raw_string = ENCODED(CharT, "abc");
 
   LIBC_NAMESPACE::printf_core::convert(&writer, raw_section);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "abc");
+  ASSERT_STREQ(str, ENCODED(CharT, "abc"));
   ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, PercentConversion) {
-  LIBC_NAMESPACE::printf_core::FormatSection simple_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, PercentConversion, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+
+  FormatSection<CharT> simple_conv;
   simple_conv.has_conv = true;
-  simple_conv.raw_string = "%%";
-  simple_conv.conv_name = '%';
+  simple_conv.raw_string = ENCODED(CharT, "%%");
+  simple_conv.conv_name = ENCODED(CharT, '%');
 
   LIBC_NAMESPACE::printf_core::convert(&writer, simple_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "%");
+  ASSERT_STREQ(str, ENCODED(CharT, "%"));
   ASSERT_EQ(writer.get_chars_written(), size_t{1});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, CharConversionSimple) {
-  LIBC_NAMESPACE::printf_core::FormatSection simple_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, CharConversionSimple, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> simple_conv;
   simple_conv.has_conv = true;
   // If has_conv is true, the raw string is ignored. They are not being parsed
   // and match the actual conversion taking place so that you can compare these
   // tests with other implmentations. The raw strings are completely optional.
-  simple_conv.raw_string = "%c";
-  simple_conv.conv_name = 'c';
+  simple_conv.raw_string = ENCODED(CharT, "%c");
+  simple_conv.conv_name = ENCODED(CharT, 'c');
   simple_conv.conv_val_raw = 'D';
 
   LIBC_NAMESPACE::printf_core::convert(&writer, simple_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "D");
+  ASSERT_STREQ(str, ENCODED(CharT, "D"));
   ASSERT_EQ(writer.get_chars_written(), size_t{1});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, CharConversionRightJustified) {
-  LIBC_NAMESPACE::printf_core::FormatSection right_justified_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, CharConversionRightJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> right_justified_conv;
   right_justified_conv.has_conv = true;
-  right_justified_conv.raw_string = "%4c";
-  right_justified_conv.conv_name = 'c';
+  right_justified_conv.raw_string = ENCODED(CharT, "%4c");
+  right_justified_conv.conv_name = ENCODED(CharT, 'c');
   right_justified_conv.min_width = 4;
   right_justified_conv.conv_val_raw = 'E';
   LIBC_NAMESPACE::printf_core::convert(&writer, right_justified_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "   E");
+  ASSERT_STREQ(str, ENCODED(CharT, "   E"));
   ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, CharConversionLeftJustified) {
-  LIBC_NAMESPACE::printf_core::FormatSection left_justified_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, CharConversionLeftJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> left_justified_conv;
   left_justified_conv.has_conv = true;
-  left_justified_conv.raw_string = "%-4c";
-  left_justified_conv.conv_name = 'c';
+  left_justified_conv.raw_string = ENCODED(CharT, "%-4c");
+  left_justified_conv.conv_name = ENCODED(CharT, 'c');
   left_justified_conv.flags =
       LIBC_NAMESPACE::printf_core::FormatFlags::LEFT_JUSTIFIED;
   left_justified_conv.min_width = 4;
   left_justified_conv.conv_val_raw = 'F';
   LIBC_NAMESPACE::printf_core::convert(&writer, left_justified_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "F   "));
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
+}
+
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+
+TEST(LlvmLibcPrintfConverterTest, CharConversionInvalidAscii) {
+  // For *wprintf functions, a "%c" conversion should convert the argument to
+  // wchar_t as if by calling btowc(), which is an error for an invalid
+  // multibyte sequence of length 1 (i.e. not a valid ASCII value).
+
+  wchar_t str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  FormatSection<wchar_t> invalid_conv;
+  invalid_conv.has_conv = true;
+  invalid_conv.raw_string = L"%c";
+  invalid_conv.conv_name = L'c';
+  invalid_conv.conv_val_raw = 128;
+  ASSERT_LT(LIBC_NAMESPACE::printf_core::convert(&writer, invalid_conv), 0);
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideCharConversionSimple,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> simple_conv;
+  simple_conv.has_conv = true;
+  // If has_conv is true, the raw string is ignored. They are not being parsed
+  // and match the actual conversion taking place so that you can compare these
+  // tests with other implmentations. The raw strings are completely optional.
+  simple_conv.raw_string = ENCODED(CharT, "%lc");
+  simple_conv.length_modifier = LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  simple_conv.conv_name = ENCODED(CharT, 'c');
+  simple_conv.conv_val_raw = L'D';
+
+  LIBC_NAMESPACE::printf_core::convert(&writer, simple_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "D"));
+  ASSERT_EQ(writer.get_chars_written(), size_t{1});
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideCharConversionRightJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> right_justified_conv;
+  right_justified_conv.has_conv = true;
+  right_justified_conv.raw_string = ENCODED(CharT, "%4lc");
+  right_justified_conv.length_modifier =
+      LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  right_justified_conv.conv_name = ENCODED(CharT, 'c');
+  right_justified_conv.min_width = 4;
+  right_justified_conv.conv_val_raw = L'E';
+  LIBC_NAMESPACE::printf_core::convert(&writer, right_justified_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "   E"));
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideCharConversionLeftJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> left_justified_conv;
+  left_justified_conv.has_conv = true;
+  left_justified_conv.raw_string = ENCODED(CharT, "%-4lc");
+  left_justified_conv.length_modifier =
+      LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  left_justified_conv.conv_name = ENCODED(CharT, 'c');
+  left_justified_conv.flags =
+      LIBC_NAMESPACE::printf_core::FormatFlags::LEFT_JUSTIFIED;
+  left_justified_conv.min_width = 4;
+  left_justified_conv.conv_val_raw = L'F';
+  LIBC_NAMESPACE::printf_core::convert(&writer, left_justified_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "F   ");
+  ASSERT_STREQ(str, ENCODED(CharT, "F   "));
   ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, StringConversionSimple) {
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionSimple, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
 
-  LIBC_NAMESPACE::printf_core::FormatSection simple_conv;
+  FormatSection<CharT> simple_conv;
   simple_conv.has_conv = true;
-  simple_conv.raw_string = "%s";
-  simple_conv.conv_name = 's';
+  simple_conv.raw_string = ENCODED(CharT, "%s");
+  simple_conv.conv_name = ENCODED(CharT, 's');
   simple_conv.conv_val_ptr = const_cast<char *>("DEF");
 
   LIBC_NAMESPACE::printf_core::convert(&writer, simple_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "DEF");
+  ASSERT_STREQ(str, ENCODED(CharT, "DEF"));
   ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionHigh) {
-  LIBC_NAMESPACE::printf_core::FormatSection high_precision_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionPrecisionHigh,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> high_precision_conv;
   high_precision_conv.has_conv = true;
-  high_precision_conv.raw_string = "%.4s";
-  high_precision_conv.conv_name = 's';
+  high_precision_conv.raw_string = ENCODED(CharT, "%.4s");
+  high_precision_conv.conv_name = ENCODED(CharT, 's');
   high_precision_conv.precision = 4;
   high_precision_conv.conv_val_ptr = const_cast<char *>("456");
   LIBC_NAMESPACE::printf_core::convert(&writer, high_precision_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "456");
+  ASSERT_STREQ(str, ENCODED(CharT, "456"));
   ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow) {
-  LIBC_NAMESPACE::printf_core::FormatSection low_precision_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> low_precision_conv;
   low_precision_conv.has_conv = true;
-  low_precision_conv.raw_string = "%.2s";
-  low_precision_conv.conv_name = 's';
+  low_precision_conv.raw_string = ENCODED(CharT, "%.2s");
+  low_precision_conv.conv_name = ENCODED(CharT, 's');
   low_precision_conv.precision = 2;
   low_precision_conv.conv_val_ptr = const_cast<char *>("xyz");
   LIBC_NAMESPACE::printf_core::convert(&writer, low_precision_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "xy");
+  ASSERT_STREQ(str, ENCODED(CharT, "xy"));
   ASSERT_EQ(writer.get_chars_written(), size_t{2});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, StringConversionRightJustified) {
-  LIBC_NAMESPACE::printf_core::FormatSection right_justified_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionRightJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> right_justified_conv;
   right_justified_conv.has_conv = true;
-  right_justified_conv.raw_string = "%4s";
-  right_justified_conv.conv_name = 's';
+  right_justified_conv.raw_string = ENCODED(CharT, "%4s");
+  right_justified_conv.conv_name = ENCODED(CharT, 's');
   right_justified_conv.min_width = 4;
   right_justified_conv.conv_val_ptr = const_cast<char *>("789");
   LIBC_NAMESPACE::printf_core::convert(&writer, right_justified_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, " 789");
+  ASSERT_STREQ(str, ENCODED(CharT, " 789"));
   ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, StringConversionLeftJustified) {
-  LIBC_NAMESPACE::printf_core::FormatSection left_justified_conv;
+TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionLeftJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> left_justified_conv;
   left_justified_conv.has_conv = true;
-  left_justified_conv.raw_string = "%-4s";
-  left_justified_conv.conv_name = 's';
+  left_justified_conv.raw_string = ENCODED(CharT, "%-4s");
+  left_justified_conv.conv_name = ENCODED(CharT, 's');
   left_justified_conv.flags =
       LIBC_NAMESPACE::printf_core::FormatFlags::LEFT_JUSTIFIED;
   left_justified_conv.min_width = 4;
   left_justified_conv.conv_val_ptr = const_cast<char *>("ghi");
   LIBC_NAMESPACE::printf_core::convert(&writer, left_justified_conv);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "ghi ");
+  ASSERT_STREQ(str, ENCODED(CharT, "ghi "));
   ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
-  LIBC_NAMESPACE::printf_core::FormatSection section;
+#if !defined(LIBC_COPT_PRINTF_DISABLE_WIDE)
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideStringConversionSimple,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+
+  FormatSection<CharT> simple_conv;
+  simple_conv.has_conv = true;
+  simple_conv.raw_string = ENCODED(CharT, "%ls");
+  simple_conv.length_modifier = LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  simple_conv.conv_name = ENCODED(CharT, 's');
+  simple_conv.conv_val_ptr = const_cast<wchar_t *>(L"DEF");
+
+  LIBC_NAMESPACE::printf_core::convert(&writer, simple_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "DEF"));
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideStringConversionPrecisionHigh,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> high_precision_conv;
+  high_precision_conv.has_conv = true;
+  high_precision_conv.raw_string = ENCODED(CharT, "%.4ls");
+  high_precision_conv.length_modifier =
+      LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  high_precision_conv.conv_name = ENCODED(CharT, 's');
+  high_precision_conv.precision = 4;
+  high_precision_conv.conv_val_ptr = const_cast<wchar_t *>(L"456");
+  LIBC_NAMESPACE::printf_core::convert(&writer, high_precision_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "456"));
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideStringConversionPrecisionLow,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> low_precision_conv;
+  low_precision_conv.has_conv = true;
+  low_precision_conv.raw_string = ENCODED(CharT, "%.2ls");
+  low_precision_conv.length_modifier =
+      LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  low_precision_conv.conv_name = ENCODED(CharT, 's');
+  low_precision_conv.precision = 2;
+  low_precision_conv.conv_val_ptr = const_cast<wchar_t *>(L"xyz");
+  LIBC_NAMESPACE::printf_core::convert(&writer, low_precision_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "xy"));
+  ASSERT_EQ(writer.get_chars_written(), size_t{2});
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideStringConversionRightJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> right_justified_conv;
+  right_justified_conv.has_conv = true;
+  right_justified_conv.raw_string = ENCODED(CharT, "%4ls");
+  right_justified_conv.length_modifier =
+      LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  right_justified_conv.conv_name = ENCODED(CharT, 's');
+  right_justified_conv.min_width = 4;
+  right_justified_conv.conv_val_ptr = const_cast<wchar_t *>(L"789");
+  LIBC_NAMESPACE::printf_core::convert(&writer, right_justified_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, " 789"));
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
+}
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, WideStringConversionLeftJustified,
+           TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> left_justified_conv;
+  left_justified_conv.has_conv = true;
+  left_justified_conv.raw_string = ENCODED(CharT, "%-4ls");
+  left_justified_conv.length_modifier =
+      LIBC_NAMESPACE::printf_core::LengthModifier::l;
+  left_justified_conv.conv_name = ENCODED(CharT, 's');
+  left_justified_conv.flags =
+      LIBC_NAMESPACE::printf_core::FormatFlags::LEFT_JUSTIFIED;
+  left_justified_conv.min_width = 4;
+  left_justified_conv.conv_val_ptr = const_cast<wchar_t *>(L"ghi");
+  LIBC_NAMESPACE::printf_core::convert(&writer, left_justified_conv);
+
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+  ASSERT_STREQ(str, ENCODED(CharT, "ghi "));
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
+}
+
+#endif // !LIBC_COPT_PRINTF_DISABLE_WIDE
+
+TYPED_TEST(LlvmLibcPrintfConverterTest, IntConversionSimple, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> section;
   section.has_conv = true;
-  section.raw_string = "%d";
-  section.conv_name = 'd';
+  section.raw_string = ENCODED(CharT, "%d");
+  section.conv_name = ENCODED(CharT, 'd');
   section.conv_val_raw = 12345;
   LIBC_NAMESPACE::printf_core::convert(&writer, section);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "12345");
+  ASSERT_STREQ(str, ENCODED(CharT, "12345"));
   ASSERT_EQ(writer.get_chars_written(), size_t{5});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, HexConversion) {
-  LIBC_NAMESPACE::printf_core::FormatSection section;
+TYPED_TEST(LlvmLibcPrintfConverterTest, HexConversion, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> section;
   section.has_conv = true;
-  section.raw_string = "%#018x";
-  section.conv_name = 'x';
+  section.raw_string = ENCODED(CharT, "%#018x");
+  section.conv_name = ENCODED(CharT, 'x');
   section.flags = static_cast<LIBC_NAMESPACE::printf_core::FormatFlags>(
       LIBC_NAMESPACE::printf_core::FormatFlags::ALTERNATE_FORM |
       LIBC_NAMESPACE::printf_core::FormatFlags::LEADING_ZEROES);
@@ -207,49 +471,63 @@ TEST_F(LlvmLibcPrintfConverterTest, HexConversion) {
   section.conv_val_raw = 0x123456ab;
   LIBC_NAMESPACE::printf_core::convert(&writer, section);
 
-  wb.buff[wb.buff_cur] = '\0';
-  ASSERT_STREQ(str, "0x00000000123456ab");
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+  ASSERT_STREQ(str, ENCODED(CharT, "0x00000000123456ab"));
   ASSERT_EQ(writer.get_chars_written(), size_t{18});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, BinaryConversion) {
-  LIBC_NAMESPACE::printf_core::FormatSection section;
+TYPED_TEST(LlvmLibcPrintfConverterTest, BinaryConversion, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
+  FormatSection<CharT> section;
   section.has_conv = true;
-  section.raw_string = "%b";
-  section.conv_name = 'b';
+  section.raw_string = ENCODED(CharT, "%b");
+  section.conv_name = ENCODED(CharT, 'b');
   section.conv_val_raw = 42;
   LIBC_NAMESPACE::printf_core::convert(&writer, section);
 
-  wb.buff[wb.buff_cur] = '\0';
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
 
-  ASSERT_STREQ(str, "101010");
+  ASSERT_STREQ(str, ENCODED(CharT, "101010"));
   ASSERT_EQ(writer.get_chars_written(), size_t{6});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, PointerConversion) {
+TYPED_TEST(LlvmLibcPrintfConverterTest, PointerConversion, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
 
-  LIBC_NAMESPACE::printf_core::FormatSection section;
+  FormatSection<CharT> section;
   section.has_conv = true;
-  section.raw_string = "%p";
-  section.conv_name = 'p';
+  section.raw_string = ENCODED(CharT, "%p");
+  section.conv_name = ENCODED(CharT, 'p');
   section.conv_val_ptr = (void *)(0x123456ab);
   LIBC_NAMESPACE::printf_core::convert(&writer, section);
 
-  wb.buff[wb.buff_cur] = '\0';
-  ASSERT_STREQ(str, "0x123456ab");
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+  ASSERT_STREQ(str, ENCODED(CharT, "0x123456ab"));
   ASSERT_EQ(writer.get_chars_written(), size_t{10});
 }
 
-TEST_F(LlvmLibcPrintfConverterTest, OctConversion) {
+TYPED_TEST(LlvmLibcPrintfConverterTest, OctConversion, TestCharTypes) {
+  using CharT = ParamType;
+  CharT str[60];
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<CharT> &wb = writer.get_write_buffer();
 
-  LIBC_NAMESPACE::printf_core::FormatSection section;
+  FormatSection<CharT> section;
   section.has_conv = true;
-  section.raw_string = "%o";
-  section.conv_name = 'o';
+  section.raw_string = ENCODED(CharT, "%o");
+  section.conv_name = ENCODED(CharT, 'o');
   section.conv_val_raw = 01234;
   LIBC_NAMESPACE::printf_core::convert(&writer, section);
 
-  wb.buff[wb.buff_cur] = '\0';
-  ASSERT_STREQ(str, "1234");
+  wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+  ASSERT_STREQ(str, ENCODED(CharT, "1234"));
   ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
+
+} // namespace

diff  --git a/libc/test/src/__support/printf_core/parser_test.cpp b/libc/test/src/__support/printf_core/parser_test.cpp
index 3853395f70afe3..40e9c36c2e1523 100644
--- a/libc/test/src/__support/printf_core/parser_test.cpp
+++ b/libc/test/src/__support/printf_core/parser_test.cpp
@@ -31,9 +31,8 @@ template <typename CharT> void init(const CharT *__restrict str, ...) {
 }
 
 template <typename CharT>
-void evaluate(
-    LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> *format_arr,
-    const CharT *__restrict str, ...) {
+void evaluate(LIBC_NAMESPACE::printf_core::FormatSection<CharT> *format_arr,
+              const CharT *__restrict str, ...) {
   va_list vlist;
   va_start(vlist, str);
   ArgList v(vlist);
@@ -62,11 +61,11 @@ TYPED_TEST(LlvmLibcPrintfParserTest, Constructor, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalRaw, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "test");
   evaluate(format_arr, str);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = false;
 
   expected.raw_string = {str, 4};
@@ -77,11 +76,11 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalRaw, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalSimple, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "test %% test");
   evaluate(format_arr, str);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1,
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1,
       expected2;
   expected0.has_conv = false;
 
@@ -105,12 +104,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalSimple, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArg, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 2};
@@ -123,12 +122,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArg, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalBadArg, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%\0abc");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = false;
   expected.raw_string = {str, 1};
 
@@ -137,12 +136,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalBadArg, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithFlags, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%+-0 #d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 7};
@@ -161,12 +160,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithFlags, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithWidth, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%12d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 4};
@@ -180,12 +179,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithWidth, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithPrecision, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%.34d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 5};
@@ -200,12 +199,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithPrecision, TestCharTypes) {
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithTrivialPrecision,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%.d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 3};
@@ -220,12 +219,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithTrivialPrecision,
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithShortLengthModifier,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%hd");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 3};
@@ -240,12 +239,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithShortLengthModifier,
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithLongLengthModifier,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%lld");
   long long arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 4};
@@ -261,12 +260,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithLongLengthModifier,
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithBitWidthLengthModifier,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%w32d");
   long long arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 5};
@@ -282,12 +281,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithBitWidthLengthModifier,
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithFastBitWidthLengthModifier,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%wf32d");
   long long arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 6};
@@ -303,12 +302,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithFastBitWidthLengthModifier,
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithAllOptions, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "% -056.78jd");
   intmax_t arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 11};
@@ -328,14 +327,14 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithAllOptions, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalThreeArgs, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%d%f%s");
   int arg1 = 12345;
   double arg2 = 123.45;
   const CharT *arg3 = ENCODED(CharT, "12345");
   evaluate(format_arr, str, arg1, arg2, arg3);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1,
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1,
       expected2;
   expected0.has_conv = true;
 
@@ -366,12 +365,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalThreeArgs, TestCharTypes) {
 TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithOverflowingWidthAndPrecision,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%-999999999999.999999999999d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 28};
@@ -388,14 +387,14 @@ TYPED_TEST(LlvmLibcPrintfParserTest, EvalOneArgWithOverflowingWidthAndPrecision,
 TYPED_TEST(LlvmLibcPrintfParserTest,
            EvalOneArgWithOverflowingWidthAndPrecisionAsArgs, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%*.*d");
   int arg1 = INT_MIN; // INT_MIN = -2147483648 if int is 32 bits.
   int arg2 = INT_MIN;
   int arg3 = 12345;
   evaluate(format_arr, str, arg1, arg2, arg3);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 5};
@@ -413,12 +412,12 @@ TYPED_TEST(LlvmLibcPrintfParserTest,
 
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeOneArg, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%1$d");
   int arg1 = 12345;
   evaluate(format_arr, str, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
   expected.has_conv = true;
 
   expected.raw_string = {str, 4};
@@ -432,14 +431,14 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeOneArg, TestCharTypes) {
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeThreeArgsSequential,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%1$d%2$f%3$s");
   int arg1 = 12345;
   double arg2 = 123.45;
   const CharT *arg3 = ENCODED(CharT, "12345");
   evaluate(format_arr, str, arg1, arg2, arg3);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1,
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1,
       expected2;
   expected0.has_conv = true;
 
@@ -469,14 +468,14 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeThreeArgsSequential,
 
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeThreeArgsReverse, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%3$d%2$f%1$s");
   int arg1 = 12345;
   double arg2 = 123.45;
   const CharT *arg3 = ENCODED(CharT, "12345");
   evaluate(format_arr, str, arg3, arg2, arg1);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1,
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1,
       expected2;
   expected0.has_conv = true;
 
@@ -506,7 +505,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeThreeArgsReverse, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeTenArgsRandom, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str =
       ENCODED(CharT, "%6$d%3$d%7$d%2$d%8$d%1$d%4$d%9$d%5$d%10$d");
   int args[10] = {6, 4, 2, 7, 9, 1, 3, 5, 8, 10};
@@ -514,7 +513,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeTenArgsRandom, TestCharTypes) {
            args[5], args[6], args[7], args[8], args[9]);
 
   for (size_t i = 0; i < 10; ++i) {
-    LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected;
+    LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected;
     expected.has_conv = true;
 
     expected.raw_string = {str + (4 * i),
@@ -527,7 +526,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeTenArgsRandom, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeComplexParsing, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str =
       ENCODED(CharT, "normal text %3$llu %% %2$ *4$f %2$ .*4$f %1$1.1c");
   CharT arg1 = ENCODED(CharT, '1');
@@ -536,7 +535,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeComplexParsing, TestCharTypes) {
   int arg4 = 10;
   evaluate(format_arr, str, arg1, arg2, arg3, arg4);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1,
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1,
       expected2, expected3, expected4, expected5, expected6, expected7,
       expected8, expected9;
 
@@ -621,7 +620,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeComplexParsing, TestCharTypes) {
 
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeGapCheck, TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%1$d%2$d%4$d");
   int arg1 = 1;
   int arg2 = 2;
@@ -630,7 +629,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeGapCheck, TestCharTypes) {
 
   evaluate(format_arr, str, arg1, arg2, arg3, arg4);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1,
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1,
       expected2;
 
   expected0.has_conv = true;
@@ -658,11 +657,11 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeGapCheck, TestCharTypes) {
 TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeTrailingPercentCrash,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
   const CharT *str = ENCODED(CharT, "%2$d%");
   evaluate(format_arr, str, 1, 2);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0, expected1;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0, expected1;
   expected0.has_conv = false;
 
   expected0.raw_string = {str, 4};
@@ -677,7 +676,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, IndexModeTrailingPercentCrash,
 TYPED_TEST(LlvmLibcPrintfParserTest, DoublePercentIsAllowedInvalidIndex,
            TestCharTypes) {
   using CharT = ParamType;
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> format_arr[10];
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> format_arr[10];
 
   // Normally this conversion specifier would be raw (due to having a width
   // defined as an invalid argument) but since it's a % conversion it's allowed
@@ -693,7 +692,7 @@ TYPED_TEST(LlvmLibcPrintfParserTest, DoublePercentIsAllowedInvalidIndex,
 
   evaluate(format_arr, str, 1, 2);
 
-  LIBC_NAMESPACE::printf_core::BasicFormatSection<CharT> expected0;
+  LIBC_NAMESPACE::printf_core::FormatSection<CharT> expected0;
   expected0.has_conv = true;
 
   expected0.raw_string = str;


        


More information about the libc-commits mailing list