[libc-commits] [libc] ff6fe39 - [libc] add sprintf
Michael Jones via libc-commits
libc-commits at lists.llvm.org
Tue May 17 11:32:25 PDT 2022
Author: Michael Jones
Date: 2022-05-17T11:32:20-07:00
New Revision: ff6fe39eca705186fab1f06376cc4b648ff9711b
URL: https://github.com/llvm/llvm-project/commit/ff6fe39eca705186fab1f06376cc4b648ff9711b
DIFF: https://github.com/llvm/llvm-project/commit/ff6fe39eca705186fab1f06376cc4b648ff9711b.diff
LOG: [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
Added:
libc/src/stdio/sprintf.cpp
libc/src/stdio/sprintf.h
libc/test/src/stdio/sprintf_test.cpp
Modified:
libc/config/linux/x86_64/entrypoints.txt
libc/spec/stdc.td
libc/src/stdio/CMakeLists.txt
libc/src/stdio/printf_core/CMakeLists.txt
libc/src/stdio/printf_core/char_converter.h
libc/src/stdio/printf_core/converter.cpp
libc/src/stdio/printf_core/converter.h
libc/src/stdio/printf_core/printf_main.h
libc/src/stdio/printf_core/string_converter.h
libc/test/src/stdio/CMakeLists.txt
libc/test/src/stdio/printf_core/parser_test.cpp
Removed:
################################################################################
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 6ad45a3d5f4d..d1edcd16c835 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -268,6 +268,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.funlockfile
libc.src.stdio.fwrite
libc.src.stdio.fwrite_unlocked
+ libc.src.stdio.sprintf
# signal.h entrypoints
# TODO: Enable signal.h entrypoints after fixing signal.h
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 29fdc25f5ae7..3becf12dfc86 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -532,6 +532,13 @@ def StdC : StandardSpec<"stdc"> {
ArgSpec<SizeTType>,
ArgSpec<FILERestrictedPtr>]
>,
+ FunctionSpec<
+ "sprintf",
+ RetValSpec<IntType>,
+ [ArgSpec<CharRestrictedPtr>,
+ ArgSpec<ConstCharRestrictedPtr>,
+ ArgSpec<VarArgType>]
+ >,
]
>;
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index ae614d81715f..9f64d18d4d1d 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -202,3 +202,17 @@ add_entrypoint_object(
libc.include.stdio
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
+)
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index a386afcdf684..7c9853ee3370 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -51,3 +51,16 @@ add_object_library(
.writer
.core_structs
)
+
+
+add_header_library(
+ printf_main
+ HDRS
+ printf_main.h
+ DEPENDS
+ .parser
+ .converter
+ .writer
+ .core_structs
+ libc.src.__support.arg_list
+)
diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 45740455610a..d4ae6e04e5a9 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -12,7 +12,7 @@
namespace __llvm_libc {
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;
if (to_conv.min_width > 1) {
diff --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
index cc684b789bd5..856f85f82b70 100644
--- a/libc/src/stdio/printf_core/converter.cpp
+++ b/libc/src/stdio/printf_core/converter.cpp
@@ -24,7 +24,7 @@
namespace __llvm_libc {
namespace printf_core {
-void convert(Writer *writer, FormatSection to_conv) {
+void convert(Writer *writer, const FormatSection &to_conv) {
if (!to_conv.has_conv) {
writer->write(to_conv.raw_string, to_conv.raw_len);
return;
diff --git a/libc/src/stdio/printf_core/converter.h b/libc/src/stdio/printf_core/converter.h
index 15e4e8d80f5d..52f506e340d3 100644
--- a/libc/src/stdio/printf_core/converter.h
+++ b/libc/src/stdio/printf_core/converter.h
@@ -20,7 +20,7 @@ namespace printf_core {
// convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the
// writer.
-void convert(Writer *writer, FormatSection to_conv);
+void convert(Writer *writer, const FormatSection &to_conv);
} // namespace printf_core
} // namespace __llvm_libc
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 78057f0a0ff7..84ca69a5ce03 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -21,7 +21,7 @@ namespace __llvm_libc {
namespace printf_core {
int printf_main(Writer *writer, const char *__restrict str,
- internal::ArgList args) {
+ internal::ArgList &args) {
Parser parser(str, args);
for (FormatSection cur_section = parser.get_next_section();
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index b0a918869841..d3ceda283a5e 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -14,7 +14,7 @@
namespace __llvm_libc {
namespace printf_core {
-void convert_string(Writer *writer, FormatSection to_conv) {
+void convert_string(Writer *writer, const FormatSection &to_conv) {
int string_len = 0;
for (char *cur_str = reinterpret_cast<char *>(to_conv.conv_val_ptr);
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
new file mode 100644
index 000000000000..a05c431451a5
--- /dev/null
+++ b/libc/src/stdio/sprintf.cpp
@@ -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
diff --git a/libc/src/stdio/sprintf.h b/libc/src/stdio/sprintf.h
new file mode 100644
index 000000000000..234de6e16522
--- /dev/null
+++ b/libc/src/stdio/sprintf.h
@@ -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
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 538cb3f1624f..e9f458539d1f 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -63,6 +63,16 @@ add_libc_unittest(
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(testdata)
diff --git a/libc/test/src/stdio/printf_core/parser_test.cpp b/libc/test/src/stdio/printf_core/parser_test.cpp
index e149acb6658d..c6ec04a0e7d2 100644
--- a/libc/test/src/stdio/printf_core/parser_test.cpp
+++ b/libc/test/src/stdio/printf_core/parser_test.cpp
@@ -53,6 +53,7 @@ TEST(LlvmLibcPrintfParserTest, EvalRaw) {
expected.raw_string = str;
ASSERT_FORMAT_EQ(expected, format_arr[0]);
+ // TODO: add checks that the format_arr after the last one has length 0
}
TEST(LlvmLibcPrintfParserTest, EvalSimple) {
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
new file mode 100644
index 000000000000..5714bd95eb54
--- /dev/null
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -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
More information about the libc-commits
mailing list