[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