-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc] Add vsscanf function #101402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
[libc] Add vsscanf function #101402
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Summary: Adds support for the `vsscanf` function similar to `sscanf`. Based off of llvm#97529.
@llvm/pr-subscribers-libc Author: Joseph Huber (jhuber6) ChangesSummary: Full diff: https://github.com/llvm/llvm-project/pull/101402.diff 10 Files Affected:
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 084f899c2b957..aa8edc188eeee 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 dbd9cf07d6b7e..c9b22b5b1c701 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 6aaf05ffd9f65..20d32615a5083 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -917,6 +917,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);
+}
|
michaelrj-google
approved these changes
Jul 31, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
SchrodingerZhu
approved these changes
Jul 31, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary:
Adds support for the
vsscanf
function similar tosscanf
.Based off of #97529.