[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