Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libc/src/stdio/printf_core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ add_header_library(
.printf_config
.writer
libc.include.inttypes
libc.hdr.types.wchar_t
libc.src.__support.big_int
libc.src.__support.common
libc.src.__support.CPP.limits
Expand All @@ -123,6 +124,8 @@ add_header_library(
libc.src.__support.integer_to_string
libc.src.__support.libc_assert
libc.src.__support.uint128
libc.src.__support.wchar.mbstate
libc.src.__support.wchar.wcrtomb
libc.src.__support.StringUtil.error_to_string
libc.src.string.memory_utils.inline_memcpy
)
Expand Down
28 changes: 24 additions & 4 deletions libc/src/stdio/printf_core/char_converter.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===-- String Converter for printf -----------------------------*- C++ -*-===//
//===-- Character Converter for printf --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand All @@ -9,7 +9,10 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H

#include "hdr/types/wchar_t.h"
#include "src/__support/macros/config.h"
#include "src/__support/wchar/mbstate.h"
#include "src/__support/wchar/wcrtomb.h"
#include "src/stdio/printf_core/converter_utils.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/writer.h"
Expand All @@ -20,8 +23,11 @@ namespace printf_core {
template <WriteMode write_mode>
LIBC_INLINE int convert_char(Writer<write_mode> *writer,
const FormatSection &to_conv) {
char c = static_cast<char>(to_conv.conv_val_raw);

char c;
wchar_t wc;
char mb_str[MB_LEN_MAX];
internal::mbstate internal_mbstate = {0};
int ret = 0;
constexpr int STRING_LEN = 1;

size_t padding_spaces =
Expand All @@ -33,7 +39,21 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
}

RET_IF_RESULT_NEGATIVE(writer->write(c));
if (to_conv.length_modifier == LengthModifier::l) {
wc = static_cast<wchar_t>(static_cast<unsigned int>(to_conv.conv_val_raw));
ret = internal::wcrtomb(mb_str, wc, &internal_mbstate);
if (ret <= 0) {
return -1;
}

for (int i = 0; i < ret; i++) {
RET_IF_RESULT_NEGATIVE(writer->write(mb_str[i]));
}

} else {
c = static_cast<char>(to_conv.conv_val_raw);
RET_IF_RESULT_NEGATIVE(writer->write(c));
}

// If the padding is on the right side, write the spaces last.
if (padding_spaces > 0 &&
Expand Down
1 change: 1 addition & 0 deletions libc/test/src/stdio/printf_core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ add_libc_unittest(
libc.src.stdio.printf_core.converter
libc.src.stdio.printf_core.writer
libc.src.stdio.printf_core.core_structs
libc.hdr.types.wchar_t
)
55 changes: 54 additions & 1 deletion libc/test/src/stdio/printf_core/converter_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
//
//===----------------------------------------------------------------------===//

#include "hdr/types/wchar_t.h"
#include "src/stdio/printf_core/converter.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/writer.h"

#include "test/UnitTest/Test.h"

class LlvmLibcPrintfConverterTest : public LIBC_NAMESPACE::testing::Test {
Expand Down Expand Up @@ -255,3 +255,56 @@ TEST_F(LlvmLibcPrintfConverterTest, OctConversion) {
ASSERT_STREQ(str, "1234");
ASSERT_EQ(writer.get_chars_written(), size_t{4});
}

TEST_F(LlvmLibcPrintfConverterTest, WideCharConversion) {

LIBC_NAMESPACE::printf_core::FormatSection section;
section.has_conv = true;
section.raw_string = "%c";
section.conv_name = 'c';
section.length_modifier = LIBC_NAMESPACE::printf_core::LengthModifier::l;
section.conv_val_raw = static_cast<wchar_t>(L'S');

LIBC_NAMESPACE::printf_core::convert(&writer, section);

wb.buff[wb.buff_cur] = '\0';

ASSERT_STREQ(str, "S");
ASSERT_EQ(writer.get_chars_written(), size_t{1});
}

TEST_F(LlvmLibcPrintfConverterTest, WideCharConversionLeftJustified) {
LIBC_NAMESPACE::printf_core::FormatSection left_justified_conv;
left_justified_conv.has_conv = true;
left_justified_conv.raw_string = "%-4c";
left_justified_conv.conv_name = 'c';
left_justified_conv.length_modifier =
LIBC_NAMESPACE::printf_core::LengthModifier::l;
left_justified_conv.flags =
LIBC_NAMESPACE::printf_core::FormatFlags::LEFT_JUSTIFIED;
left_justified_conv.min_width = 4;
left_justified_conv.conv_val_raw = static_cast<wchar_t>(L'S');

LIBC_NAMESPACE::printf_core::convert(&writer, left_justified_conv);
wb.buff[wb.buff_cur] = '\0';

ASSERT_STREQ(str, "S ");
ASSERT_EQ(writer.get_chars_written(), size_t{4});
}

TEST_F(LlvmLibcPrintfConverterTest, WideCharConversionRightJustified) {
LIBC_NAMESPACE::printf_core::FormatSection right_justified_conv;
right_justified_conv.has_conv = true;
right_justified_conv.raw_string = "%4c";
right_justified_conv.conv_name = 'c';
right_justified_conv.length_modifier =
LIBC_NAMESPACE::printf_core::LengthModifier::l;
right_justified_conv.min_width = 4;
right_justified_conv.conv_val_raw = static_cast<wchar_t>(L'S');

LIBC_NAMESPACE::printf_core::convert(&writer, right_justified_conv);
wb.buff[wb.buff_cur] = '\0';

ASSERT_STREQ(str, " S");
ASSERT_EQ(writer.get_chars_written(), size_t{4});
}
Loading