[libc-commits] [libc] 38ef692 - [libc] Add vsscanf function (#101402)

via libc-commits libc-commits at lists.llvm.org
Wed Jul 31 14:53:28 PDT 2024


Author: Joseph Huber
Date: 2024-07-31T16:53:25-05:00
New Revision: 38ef6929a3322fdddd74b3d6abdf6936cc4d8e62

URL: https://github.com/llvm/llvm-project/commit/38ef6929a3322fdddd74b3d6abdf6936cc4d8e62
DIFF: https://github.com/llvm/llvm-project/commit/38ef6929a3322fdddd74b3d6abdf6936cc4d8e62.diff

LOG: [libc] Add vsscanf function (#101402)

Summary:
Adds support for the `vsscanf` function similar to `sscanf`.
Based off of https://github.com/llvm/llvm-project/pull/97529.

Added: 
    libc/src/stdio/vsscanf.cpp
    libc/src/stdio/vsscanf.h
    libc/test/src/stdio/vsscanf_test.cpp

Modified: 
    libc/config/gpu/entrypoints.txt
    libc/config/linux/riscv/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/newhdrgen/yaml/stdio.yaml
    libc/spec/stdc.td
    libc/src/stdio/CMakeLists.txt
    libc/test/src/stdio/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index 04a42c3019495..6035af5c0ebb0 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -188,6 +188,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.vsnprintf
     libc.src.stdio.vsprintf
     libc.src.stdio.sscanf
+    libc.src.stdio.vsscanf
     libc.src.stdio.feof
     libc.src.stdio.ferror
     libc.src.stdio.fflush

diff  --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index c229a11b5bb52..afa7f8bdd48b0 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -217,6 +217,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.snprintf
     libc.src.stdio.sprintf
     libc.src.stdio.sscanf
+    libc.src.stdio.vsscanf
     libc.src.stdio.vfprintf
     libc.src.stdio.vprintf
     libc.src.stdio.vsnprintf

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 533d0cd368863..84c52ef12f3df 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -217,6 +217,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.snprintf
     libc.src.stdio.sprintf
     libc.src.stdio.sscanf
+    libc.src.stdio.vsscanf
     libc.src.stdio.vfprintf
     libc.src.stdio.vprintf
     libc.src.stdio.vsnprintf

diff  --git a/libc/newhdrgen/yaml/stdio.yaml b/libc/newhdrgen/yaml/stdio.yaml
index 687a6d696cad6..660087e20b0cc 100644
--- a/libc/newhdrgen/yaml/stdio.yaml
+++ b/libc/newhdrgen/yaml/stdio.yaml
@@ -105,6 +105,14 @@ functions:
       - type: const char *__restrict
       - type: const char *__restrict
       - type: ...
+  - name: vsscanf
+    standards: 
+      - stdc
+    return_type: int
+    arguments:
+      - type: const char *__restrict
+      - type: const char *__restrict
+      - type: va_list
   - name: scanf
     standards: 
       - stdc

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index f3b8db598c4a1..6bb249f6bf7d7 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -920,6 +920,13 @@ def StdC : StandardSpec<"stdc"> {
                ArgSpec<ConstCharRestrictedPtr>,
                ArgSpec<VarArgType>]
           >,
+          FunctionSpec<
+              "vsscanf",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
           FunctionSpec<
               "scanf",
               RetValSpec<IntType>,

diff  --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 2d528a903cc2f..94f92351e92fa 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -121,6 +121,18 @@ add_entrypoint_object(
     libc.src.stdio.scanf_core.scanf_main
 )
 
+add_entrypoint_object(
+  vsscanf
+  SRCS
+    vsscanf.cpp
+  HDRS
+    vsscanf.h
+  DEPENDS
+    libc.src.__support.arg_list
+    libc.src.stdio.scanf_core.reader
+    libc.src.stdio.scanf_core.scanf_main
+)
+
 add_entrypoint_object(
   fscanf
   SRCS

diff  --git a/libc/src/stdio/vsscanf.cpp b/libc/src/stdio/vsscanf.cpp
new file mode 100644
index 0000000000000..fcf0b88885f17
--- /dev/null
+++ b/libc/src/stdio/vsscanf.cpp
@@ -0,0 +1,33 @@
+//===-- Implementation of vsscanf -------------------------------*- 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/vsscanf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/stdio/scanf_core/reader.h"
+#include "src/stdio/scanf_core/scanf_main.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, vsscanf,
+                   (const char *buffer, const char *format, va_list vlist)) {
+  internal::ArgList args(vlist);
+  scanf_core::ReadBuffer rb{const_cast<char *>(buffer),
+                            cpp::numeric_limits<size_t>::max()};
+  scanf_core::Reader reader(&rb);
+  int ret_val = scanf_core::scanf_main(&reader, format, args);
+  // This is done to avoid including stdio.h in the internals. On most systems
+  // EOF is -1, so this will be transformed into just "return ret_val".
+  return (ret_val == -1) ? EOF : ret_val;
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/stdio/vsscanf.h b/libc/src/stdio/vsscanf.h
new file mode 100644
index 0000000000000..992c44d3d95b9
--- /dev/null
+++ b/libc/src/stdio/vsscanf.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of vsscanf ------------------------*- 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_VSSCANF_H
+#define LLVM_LIBC_SRC_STDIO_VSSCANF_H
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE {
+
+int vsscanf(const char *s, const char *format, va_list vlist);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDIO_VSSCANF_H

diff  --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 10ec890b043a7..4ac83ec2dd600 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -282,6 +282,20 @@ add_libc_test(
     ${sscanf_test_copts}
 )
 
+add_libc_test(
+  vsscanf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    vsscanf_test.cpp
+  DEPENDS
+    libc.src.stdio.vsscanf
+  LINK_LIBRARIES
+    LibcFPTestHelpers
+  COMPILE_OPTIONS
+    ${sscanf_test_copts}
+)
+
 add_libc_test(
   puts_test
   HERMETIC_TEST_ONLY # writes to libc's stdout

diff  --git a/libc/test/src/stdio/vsscanf_test.cpp b/libc/test/src/stdio/vsscanf_test.cpp
new file mode 100644
index 0000000000000..4194e10f0602c
--- /dev/null
+++ b/libc/test/src/stdio/vsscanf_test.cpp
@@ -0,0 +1,159 @@
+//===-- Unittests for sscanf ----------------------------------------------===//
+//
+// 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/vsscanf.h"
+
+#include "test/UnitTest/Test.h"
+
+int call_vsscanf(const char *__restrict buffer, const char *__restrict format,
+                 ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  int ret = LIBC_NAMESPACE::vsscanf(buffer, format, vlist);
+  va_end(vlist);
+  return ret;
+}
+
+TEST(LlvmLibcVSScanfTest, SimpleStringConv) {
+  int ret_val;
+  char buffer[10];
+  char buffer2[10];
+  ret_val = call_vsscanf("abc123", "abc %s", buffer);
+  ASSERT_EQ(ret_val, 1);
+  ASSERT_STREQ(buffer, "123");
+
+  ret_val = call_vsscanf("abc123", "%3s %3s", buffer, buffer2);
+  ASSERT_EQ(ret_val, 2);
+  ASSERT_STREQ(buffer, "abc");
+  ASSERT_STREQ(buffer2, "123");
+
+  ret_val = call_vsscanf("abc 123", "%3s%3s", buffer, buffer2);
+  ASSERT_EQ(ret_val, 2);
+  ASSERT_STREQ(buffer, "abc");
+  ASSERT_STREQ(buffer2, "123");
+}
+
+TEST(LlvmLibcVSScanfTest, IntConvSimple) {
+  int ret_val;
+  int result = 0;
+  ret_val = call_vsscanf("123", "%d", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 123);
+
+  ret_val = call_vsscanf("456", "%i", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 456);
+
+  ret_val = call_vsscanf("789", "%x", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 0x789);
+
+  ret_val = call_vsscanf("012", "%o", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 012);
+
+  ret_val = call_vsscanf("345", "%u", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 345);
+
+  // 288 characters
+  ret_val = call_vsscanf("10000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000"
+                         "00000000000000000000000000000000",
+                         "%d", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, int(LIBC_NAMESPACE::cpp::numeric_limits<intmax_t>::max()));
+
+  ret_val = call_vsscanf("Not an integer", "%d", &result);
+  EXPECT_EQ(ret_val, 0);
+}
+
+TEST(LlvmLibcVSScanfTest, IntConvLengthModifier) {
+  int ret_val;
+  uintmax_t max_result = 0;
+  int int_result = 0;
+  char char_result = 0;
+
+  ret_val = call_vsscanf("123", "%ju", &max_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(max_result, uintmax_t(123));
+
+  // Check overflow handling
+  ret_val =
+      call_vsscanf("999999999999999999999999999999999999", "%ju", &max_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(max_result, LIBC_NAMESPACE::cpp::numeric_limits<uintmax_t>::max());
+
+  // Because this is unsigned, any out of range value should return the maximum,
+  // even with a negative sign.
+  ret_val =
+      call_vsscanf("-999999999999999999999999999999999999", "%ju", &max_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(max_result, LIBC_NAMESPACE::cpp::numeric_limits<uintmax_t>::max());
+
+  ret_val = call_vsscanf("-18446744073709551616", "%ju", &max_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(max_result, LIBC_NAMESPACE::cpp::numeric_limits<uintmax_t>::max());
+
+  // But any number below the maximum should have the - sign applied.
+  ret_val = call_vsscanf("-1", "%ju", &max_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(max_result, uintmax_t(-1));
+
+  ret_val = call_vsscanf("-1", "%u", &int_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(int_result, -1);
+
+  max_result = 0xff00ff00ff00ff00;
+  char_result = 0x6f;
+
+  // Overflows for sizes larger than the maximum are handled by casting.
+  ret_val = call_vsscanf("8589967360", "%d", &int_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(int_result, int(8589967360)); // 2^33 + 2^15
+
+  // Check that the adjacent values weren't touched by the overflow.
+  ASSERT_EQ(max_result, uintmax_t(0xff00ff00ff00ff00));
+  ASSERT_EQ(char_result, char(0x6f));
+
+  ret_val = call_vsscanf("-8589967360", "%d", &int_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(int_result, int(-8589967360));
+  ASSERT_EQ(max_result, uintmax_t(0xff00ff00ff00ff00));
+  ASSERT_EQ(char_result, char(0x6f));
+
+  ret_val = call_vsscanf("25", "%hhd", &char_result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(char_result, char(25));
+}
+
+TEST(LlvmLibcVSScanfTest, IntConvBaseSelection) {
+  int ret_val;
+  int result = 0;
+  ret_val = call_vsscanf("0xabc123", "%i", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 0xabc123);
+
+  ret_val = call_vsscanf("0456", "%i", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 0456);
+
+  ret_val = call_vsscanf("0999", "%i", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 0);
+
+  ret_val = call_vsscanf("123abc456", "%i", &result);
+  EXPECT_EQ(ret_val, 1);
+  EXPECT_EQ(result, 123);
+}


        


More information about the libc-commits mailing list