[libc] add sprintf

This adds the sprintf entrypoint, as well as unit tests. Currently
sprintf only supports %%, %s, and %c, but the other conversions are on
the way.

Reviewed By: sivachandra, lntue

Differential Revision: https://reviews.llvm.org/D125573
This commit is contained in:
Michael Jones 2022-05-12 13:43:15 -07:00
parent dbf3b5f114
commit ff6fe39eca
14 changed files with 211 additions and 5 deletions

View File

@ -268,6 +268,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.funlockfile libc.src.stdio.funlockfile
libc.src.stdio.fwrite libc.src.stdio.fwrite
libc.src.stdio.fwrite_unlocked libc.src.stdio.fwrite_unlocked
libc.src.stdio.sprintf
# signal.h entrypoints # signal.h entrypoints
# TODO: Enable signal.h entrypoints after fixing signal.h # TODO: Enable signal.h entrypoints after fixing signal.h

View File

@ -532,6 +532,13 @@ def StdC : StandardSpec<"stdc"> {
ArgSpec<SizeTType>, ArgSpec<SizeTType>,
ArgSpec<FILERestrictedPtr>] ArgSpec<FILERestrictedPtr>]
>, >,
FunctionSpec<
"sprintf",
RetValSpec<IntType>,
[ArgSpec<CharRestrictedPtr>,
ArgSpec<ConstCharRestrictedPtr>,
ArgSpec<VarArgType>]
>,
] ]
>; >;

View File

@ -202,3 +202,17 @@ add_entrypoint_object(
libc.include.stdio libc.include.stdio
libc.src.__support.File.file libc.src.__support.File.file
) )
add_entrypoint_object(
sprintf
SRCS
sprintf.cpp
HDRS
sprintf.h
DEPENDS
libc.include.stdio
libc.src.stdio.printf_core.printf_main
libc.src.stdio.printf_core.string_writer
libc.src.stdio.printf_core.writer
)

View File

@ -51,3 +51,16 @@ add_object_library(
.writer .writer
.core_structs .core_structs
) )
add_header_library(
printf_main
HDRS
printf_main.h
DEPENDS
.parser
.converter
.writer
.core_structs
libc.src.__support.arg_list
)

View File

@ -12,7 +12,7 @@
namespace __llvm_libc { namespace __llvm_libc {
namespace printf_core { namespace printf_core {
void convert_char(Writer *writer, FormatSection to_conv) { void convert_char(Writer *writer, const FormatSection &to_conv) {
char c = to_conv.conv_val_raw; char c = to_conv.conv_val_raw;
if (to_conv.min_width > 1) { if (to_conv.min_width > 1) {

View File

@ -24,7 +24,7 @@
namespace __llvm_libc { namespace __llvm_libc {
namespace printf_core { namespace printf_core {
void convert(Writer *writer, FormatSection to_conv) { void convert(Writer *writer, const FormatSection &to_conv) {
if (!to_conv.has_conv) { if (!to_conv.has_conv) {
writer->write(to_conv.raw_string, to_conv.raw_len); writer->write(to_conv.raw_string, to_conv.raw_len);
return; return;

View File

@ -20,7 +20,7 @@ namespace printf_core {
// convert will call a conversion function to convert the FormatSection into // convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the // its string representation, and then that will write the result to the
// writer. // writer.
void convert(Writer *writer, FormatSection to_conv); void convert(Writer *writer, const FormatSection &to_conv);
} // namespace printf_core } // namespace printf_core
} // namespace __llvm_libc } // namespace __llvm_libc

View File

@ -21,7 +21,7 @@ namespace __llvm_libc {
namespace printf_core { namespace printf_core {
int printf_main(Writer *writer, const char *__restrict str, int printf_main(Writer *writer, const char *__restrict str,
internal::ArgList args) { internal::ArgList &args) {
Parser parser(str, args); Parser parser(str, args);
for (FormatSection cur_section = parser.get_next_section(); for (FormatSection cur_section = parser.get_next_section();

View File

@ -14,7 +14,7 @@
namespace __llvm_libc { namespace __llvm_libc {
namespace printf_core { namespace printf_core {
void convert_string(Writer *writer, FormatSection to_conv) { void convert_string(Writer *writer, const FormatSection &to_conv) {
int string_len = 0; int string_len = 0;
for (char *cur_str = reinterpret_cast<char *>(to_conv.conv_val_ptr); for (char *cur_str = reinterpret_cast<char *>(to_conv.conv_val_ptr);

View File

@ -0,0 +1,38 @@
//===-- Implementation of sprintf -------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/stdio/sprintf.h"
#include "src/__support/arg_list.h"
#include "src/stdio/printf_core/printf_main.h"
#include "src/stdio/printf_core/string_writer.h"
#include "src/stdio/printf_core/writer.h"
#include <stdarg.h>
namespace __llvm_libc {
LLVM_LIBC_FUNCTION(int, sprintf,
(char *__restrict buffer, const char *__restrict format,
...)) {
va_list vlist;
va_start(vlist, format);
internal::ArgList args(vlist); // This holder class allows for easier copying
// and pointer semantics, as well as handing
// destruction automatically.
va_end(vlist);
printf_core::StringWriter str_writer(buffer);
printf_core::Writer writer(reinterpret_cast<void *>(&str_writer),
printf_core::write_to_string);
int ret_val = printf_core::printf_main(&writer, format, args);
str_writer.terminate();
return ret_val;
}
} // namespace __llvm_libc

18
libc/src/stdio/sprintf.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Implementation header of sprintf ------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_STDIO_SPRINTF_H
#define LLVM_LIBC_SRC_STDIO_SPRINTF_H
namespace __llvm_libc {
int sprintf(char *__restrict buffer, const char *__restrict format, ...);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_STDIO_SPRINTF_H

View File

@ -63,6 +63,16 @@ add_libc_unittest(
LibcMemoryHelpers LibcMemoryHelpers
) )
add_libc_unittest(
sprintf_test
SUITE
libc_stdio_unittests
SRCS
sprintf_test.cpp
DEPENDS
libc.src.stdio.sprintf
)
add_subdirectory(printf_core) add_subdirectory(printf_core)
add_subdirectory(testdata) add_subdirectory(testdata)

View File

@ -53,6 +53,7 @@ TEST(LlvmLibcPrintfParserTest, EvalRaw) {
expected.raw_string = str; expected.raw_string = str;
ASSERT_FORMAT_EQ(expected, format_arr[0]); ASSERT_FORMAT_EQ(expected, format_arr[0]);
// TODO: add checks that the format_arr after the last one has length 0
} }
TEST(LlvmLibcPrintfParserTest, EvalSimple) { TEST(LlvmLibcPrintfParserTest, EvalSimple) {

View File

@ -0,0 +1,104 @@
//===-- Unittests for sprintf ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/stdio/sprintf.h"
#include "utils/UnitTest/Test.h"
TEST(LlvmLibcSPrintfTest, SimpleNoConv) {
char buff[64];
int written;
written = __llvm_libc::sprintf(buff, "A simple string with no conversions.");
EXPECT_EQ(written, 36);
ASSERT_STREQ(buff, "A simple string with no conversions.");
}
TEST(LlvmLibcSPrintfTest, PercentConv) {
char buff[64];
int written;
written = __llvm_libc::sprintf(buff, "%%");
EXPECT_EQ(written, 1);
ASSERT_STREQ(buff, "%");
written = __llvm_libc::sprintf(buff, "abc %% def");
EXPECT_EQ(written, 9);
ASSERT_STREQ(buff, "abc % def");
written = __llvm_libc::sprintf(buff, "%%%%%%");
EXPECT_EQ(written, 3);
ASSERT_STREQ(buff, "%%%");
}
TEST(LlvmLibcSPrintfTest, CharConv) {
char buff[64];
int written;
written = __llvm_libc::sprintf(buff, "%c", 'a');
EXPECT_EQ(written, 1);
ASSERT_STREQ(buff, "a");
written = __llvm_libc::sprintf(buff, "%3c %-3c", '1', '2');
EXPECT_EQ(written, 7);
ASSERT_STREQ(buff, " 1 2 ");
written = __llvm_libc::sprintf(buff, "%*c", 2, '3');
EXPECT_EQ(written, 2);
ASSERT_STREQ(buff, " 3");
}
TEST(LlvmLibcSPrintfTest, StringConv) {
char buff[64];
int written;
written = __llvm_libc::sprintf(buff, "%s", "abcDEF123");
EXPECT_EQ(written, 9);
ASSERT_STREQ(buff, "abcDEF123");
written = __llvm_libc::sprintf(buff, "%10s %-10s", "centered", "title");
EXPECT_EQ(written, 21);
ASSERT_STREQ(buff, " centered title ");
written = __llvm_libc::sprintf(buff, "%-5.4s%-4.4s", "words can describe",
"soups most delicious");
EXPECT_EQ(written, 9);
ASSERT_STREQ(buff, "word soup");
written = __llvm_libc::sprintf(buff, "%*s %.*s %*.*s", 10, "beginning", 2,
"isn't", 12, 10, "important. Ever.");
EXPECT_EQ(written, 26);
ASSERT_STREQ(buff, " beginning is important.");
}
#ifndef LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE
TEST(LlvmLibcSPrintfTest, IndexModeParsing) {
char buff[64];
int written;
written = __llvm_libc::sprintf(buff, "%1$s", "abcDEF123");
EXPECT_EQ(written, 9);
ASSERT_STREQ(buff, "abcDEF123");
written = __llvm_libc::sprintf(buff, "%1$s %%", "abcDEF123");
EXPECT_EQ(written, 11);
ASSERT_STREQ(buff, "abcDEF123 %");
written =
__llvm_libc::sprintf(buff, "%3$s %1$s %2$s", "is", "hard", "ordering");
EXPECT_EQ(written, 16);
ASSERT_STREQ(buff, "ordering is hard");
written = __llvm_libc::sprintf(
buff, "%10$s %9$s %8$c %7$s %6$s, %6$s %5$s %4$-*1$s %3$.*11$s %2$s. %%",
6, "pain", "alphabetical", "such", "is", "this", "do", 'u', "would",
"why", 1);
EXPECT_EQ(written, 45);
ASSERT_STREQ(buff, "why would u do this, this is such a pain. %");
}
#endif // LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE