[libc] add snprintf
authorMichael Jones <michaelrj@google.com>
Tue, 17 May 2022 19:03:23 +0000 (12:03 -0700)
committerMichael Jones <michaelrj@google.com>
Tue, 17 May 2022 20:32:59 +0000 (13:32 -0700)
After adding sprintf, snprintf is simple. The functions are very
similar. The tests only cover the behavior of the max length since the
sprintf tests should cover the other behavior.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D125826

libc/config/linux/x86_64/entrypoints.txt
libc/spec/stdc.td
libc/src/stdio/CMakeLists.txt
libc/src/stdio/snprintf.cpp [new file with mode: 0644]
libc/src/stdio/snprintf.h [new file with mode: 0644]
libc/src/stdio/sprintf.cpp
libc/test/src/stdio/CMakeLists.txt
libc/test/src/stdio/snprintf_test.cpp [new file with mode: 0644]

index d1edcd1..da2e77e 100644 (file)
@@ -269,6 +269,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdio.fwrite
     libc.src.stdio.fwrite_unlocked
     libc.src.stdio.sprintf
+    libc.src.stdio.snprintf
 
     # signal.h entrypoints
     # TODO: Enable signal.h entrypoints after fixing signal.h
index 3becf12..aa09bc3 100644 (file)
@@ -539,6 +539,27 @@ def StdC : StandardSpec<"stdc"> {
                ArgSpec<ConstCharRestrictedPtr>,
                ArgSpec<VarArgType>]
           >,
+          FunctionSpec<
+              "snprintf",
+              RetValSpec<IntType>,
+              [ArgSpec<CharRestrictedPtr>,
+               ArgSpec<SizeTType>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VarArgType>]
+          >,
+          FunctionSpec<
+              "printf",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VarArgType>]
+          >,
+          FunctionSpec<
+              "fprintf",
+              RetValSpec<IntType>,
+              [ArgSpec<FILERestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VarArgType>]
+          >,
       ]
   >;
 
index 9f64d18..2abb509 100644 (file)
@@ -211,7 +211,18 @@ add_entrypoint_object(
   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
+)
+
+add_entrypoint_object(
+  snprintf
+  SRCS
+    snprintf.cpp
+  HDRS
+    snprintf.h
+  DEPENDS
     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/snprintf.cpp b/libc/src/stdio/snprintf.cpp
new file mode 100644 (file)
index 0000000..ecc5fc9
--- /dev/null
@@ -0,0 +1,40 @@
+//===-- Implementation of snprintf ------------------------------*- 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/snprintf.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>
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, snprintf,
+                   (char *__restrict buffer, size_t buffsz,
+                    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 handling
+                                 // destruction automatically.
+  va_end(vlist);
+  printf_core::StringWriter str_writer(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+  printf_core::Writer writer(reinterpret_cast<void *>(&str_writer),
+                             printf_core::write_to_string);
+
+  int ret_val = printf_core::printf_main(&writer, format, args);
+  if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+    str_writer.terminate();
+  return ret_val;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/snprintf.h b/libc/src/stdio/snprintf.h
new file mode 100644 (file)
index 0000000..ec223be
--- /dev/null
@@ -0,0 +1,21 @@
+//===-- Implementation header of snprintf -----------------------*- 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_SNPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_SNPRINTF_H
+
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+int snprintf(char *__restrict buffer, size_t buffsz,
+             const char *__restrict format, ...);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_SNPRINTF_H
index a05c431..442dbdf 100644 (file)
@@ -23,7 +23,7 @@ LLVM_LIBC_FUNCTION(int, sprintf,
   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
+                                 // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
   printf_core::StringWriter str_writer(buffer);
index e9f4585..331b90f 100644 (file)
@@ -73,6 +73,16 @@ add_libc_unittest(
     libc.src.stdio.sprintf
 )
 
+add_libc_unittest(
+  snprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    snprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.snprintf
+)
+
 add_subdirectory(printf_core)
 
 add_subdirectory(testdata)
diff --git a/libc/test/src/stdio/snprintf_test.cpp b/libc/test/src/stdio/snprintf_test.cpp
new file mode 100644 (file)
index 0000000..4930f37
--- /dev/null
@@ -0,0 +1,46 @@
+//===-- Unittests for snprintf --------------------------------------------===//
+//
+// 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/snprintf.h"
+
+#include "utils/UnitTest/Test.h"
+
+// The sprintf test cases cover testing the shared printf functionality, so
+// these tests will focus on snprintf exclusive features.
+
+TEST(LlvmLibcSNPrintfTest, CutOff) {
+  char buff[64];
+  int written;
+
+  written =
+      __llvm_libc::snprintf(buff, 16, "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string");
+
+  written = __llvm_libc::snprintf(buff, 5, "%s", "1234567890");
+  EXPECT_EQ(written, 10);
+  ASSERT_STREQ(buff, "1234");
+
+  // passing null as the output pointer is allowed as long as buffsz is 0.
+  written = __llvm_libc::snprintf(nullptr, 0, "%s and more", "1234567890");
+  EXPECT_EQ(written, 19);
+}
+
+TEST(LlvmLibcSNPrintfTest, NoCutOff) {
+  char buff[64];
+  int written;
+
+  written =
+      __llvm_libc::snprintf(buff, 37, "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string with no conversions.");
+
+  written = __llvm_libc::snprintf(buff, 20, "%s", "1234567890");
+  EXPECT_EQ(written, 10);
+  ASSERT_STREQ(buff, "1234567890");
+}