Skip to content

Commit 38ef692

Browse files
authored
[libc] Add vsscanf function (llvm#101402)
Summary: Adds support for the `vsscanf` function similar to `sscanf`. Based off of llvm#97529.
1 parent bf1666f commit 38ef692

File tree

10 files changed

+256
-0
lines changed

10 files changed

+256
-0
lines changed

libc/config/gpu/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ set(TARGET_LIBC_ENTRYPOINTS
188188
libc.src.stdio.vsnprintf
189189
libc.src.stdio.vsprintf
190190
libc.src.stdio.sscanf
191+
libc.src.stdio.vsscanf
191192
libc.src.stdio.feof
192193
libc.src.stdio.ferror
193194
libc.src.stdio.fflush

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ set(TARGET_LIBC_ENTRYPOINTS
217217
libc.src.stdio.snprintf
218218
libc.src.stdio.sprintf
219219
libc.src.stdio.sscanf
220+
libc.src.stdio.vsscanf
220221
libc.src.stdio.vfprintf
221222
libc.src.stdio.vprintf
222223
libc.src.stdio.vsnprintf

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ set(TARGET_LIBC_ENTRYPOINTS
217217
libc.src.stdio.snprintf
218218
libc.src.stdio.sprintf
219219
libc.src.stdio.sscanf
220+
libc.src.stdio.vsscanf
220221
libc.src.stdio.vfprintf
221222
libc.src.stdio.vprintf
222223
libc.src.stdio.vsnprintf

libc/newhdrgen/yaml/stdio.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ functions:
105105
- type: const char *__restrict
106106
- type: const char *__restrict
107107
- type: ...
108+
- name: vsscanf
109+
standards:
110+
- stdc
111+
return_type: int
112+
arguments:
113+
- type: const char *__restrict
114+
- type: const char *__restrict
115+
- type: va_list
108116
- name: scanf
109117
standards:
110118
- stdc

libc/spec/stdc.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,13 @@ def StdC : StandardSpec<"stdc"> {
920920
ArgSpec<ConstCharRestrictedPtr>,
921921
ArgSpec<VarArgType>]
922922
>,
923+
FunctionSpec<
924+
"vsscanf",
925+
RetValSpec<IntType>,
926+
[ArgSpec<ConstCharRestrictedPtr>,
927+
ArgSpec<ConstCharRestrictedPtr>,
928+
ArgSpec<VaListType>]
929+
>,
923930
FunctionSpec<
924931
"scanf",
925932
RetValSpec<IntType>,

libc/src/stdio/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,18 @@ add_entrypoint_object(
121121
libc.src.stdio.scanf_core.scanf_main
122122
)
123123

124+
add_entrypoint_object(
125+
vsscanf
126+
SRCS
127+
vsscanf.cpp
128+
HDRS
129+
vsscanf.h
130+
DEPENDS
131+
libc.src.__support.arg_list
132+
libc.src.stdio.scanf_core.reader
133+
libc.src.stdio.scanf_core.scanf_main
134+
)
135+
124136
add_entrypoint_object(
125137
fscanf
126138
SRCS

libc/src/stdio/vsscanf.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===-- Implementation of vsscanf -------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/stdio/vsscanf.h"
10+
11+
#include "src/__support/CPP/limits.h"
12+
#include "src/__support/arg_list.h"
13+
#include "src/stdio/scanf_core/reader.h"
14+
#include "src/stdio/scanf_core/scanf_main.h"
15+
16+
#include <stdarg.h>
17+
#include <stdio.h>
18+
19+
namespace LIBC_NAMESPACE_DECL {
20+
21+
LLVM_LIBC_FUNCTION(int, vsscanf,
22+
(const char *buffer, const char *format, va_list vlist)) {
23+
internal::ArgList args(vlist);
24+
scanf_core::ReadBuffer rb{const_cast<char *>(buffer),
25+
cpp::numeric_limits<size_t>::max()};
26+
scanf_core::Reader reader(&rb);
27+
int ret_val = scanf_core::scanf_main(&reader, format, args);
28+
// This is done to avoid including stdio.h in the internals. On most systems
29+
// EOF is -1, so this will be transformed into just "return ret_val".
30+
return (ret_val == -1) ? EOF : ret_val;
31+
}
32+
33+
} // namespace LIBC_NAMESPACE_DECL

libc/src/stdio/vsscanf.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header of vsscanf ------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_VSSCANF_H
10+
#define LLVM_LIBC_SRC_STDIO_VSSCANF_H
11+
12+
#include <stdarg.h>
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
int vsscanf(const char *s, const char *format, va_list vlist);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_STDIO_VSSCANF_H

libc/test/src/stdio/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,20 @@ add_libc_test(
282282
${sscanf_test_copts}
283283
)
284284

285+
add_libc_test(
286+
vsscanf_test
287+
SUITE
288+
libc_stdio_unittests
289+
SRCS
290+
vsscanf_test.cpp
291+
DEPENDS
292+
libc.src.stdio.vsscanf
293+
LINK_LIBRARIES
294+
LibcFPTestHelpers
295+
COMPILE_OPTIONS
296+
${sscanf_test_copts}
297+
)
298+
285299
add_libc_test(
286300
puts_test
287301
HERMETIC_TEST_ONLY # writes to libc's stdout

libc/test/src/stdio/vsscanf_test.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//===-- Unittests for sscanf ----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/stdio/vsscanf.h"
10+
11+
#include "test/UnitTest/Test.h"
12+
13+
int call_vsscanf(const char *__restrict buffer, const char *__restrict format,
14+
...) {
15+
va_list vlist;
16+
va_start(vlist, format);
17+
int ret = LIBC_NAMESPACE::vsscanf(buffer, format, vlist);
18+
va_end(vlist);
19+
return ret;
20+
}
21+
22+
TEST(LlvmLibcVSScanfTest, SimpleStringConv) {
23+
int ret_val;
24+
char buffer[10];
25+
char buffer2[10];
26+
ret_val = call_vsscanf("abc123", "abc %s", buffer);
27+
ASSERT_EQ(ret_val, 1);
28+
ASSERT_STREQ(buffer, "123");
29+
30+
ret_val = call_vsscanf("abc123", "%3s %3s", buffer, buffer2);
31+
ASSERT_EQ(ret_val, 2);
32+
ASSERT_STREQ(buffer, "abc");
33+
ASSERT_STREQ(buffer2, "123");
34+
35+
ret_val = call_vsscanf("abc 123", "%3s%3s", buffer, buffer2);
36+
ASSERT_EQ(ret_val, 2);
37+
ASSERT_STREQ(buffer, "abc");
38+
ASSERT_STREQ(buffer2, "123");
39+
}
40+
41+
TEST(LlvmLibcVSScanfTest, IntConvSimple) {
42+
int ret_val;
43+
int result = 0;
44+
ret_val = call_vsscanf("123", "%d", &result);
45+
EXPECT_EQ(ret_val, 1);
46+
EXPECT_EQ(result, 123);
47+
48+
ret_val = call_vsscanf("456", "%i", &result);
49+
EXPECT_EQ(ret_val, 1);
50+
EXPECT_EQ(result, 456);
51+
52+
ret_val = call_vsscanf("789", "%x", &result);
53+
EXPECT_EQ(ret_val, 1);
54+
EXPECT_EQ(result, 0x789);
55+
56+
ret_val = call_vsscanf("012", "%o", &result);
57+
EXPECT_EQ(ret_val, 1);
58+
EXPECT_EQ(result, 012);
59+
60+
ret_val = call_vsscanf("345", "%u", &result);
61+
EXPECT_EQ(ret_val, 1);
62+
EXPECT_EQ(result, 345);
63+
64+
// 288 characters
65+
ret_val = call_vsscanf("10000000000000000000000000000000"
66+
"00000000000000000000000000000000"
67+
"00000000000000000000000000000000"
68+
"00000000000000000000000000000000"
69+
"00000000000000000000000000000000"
70+
"00000000000000000000000000000000"
71+
"00000000000000000000000000000000"
72+
"00000000000000000000000000000000"
73+
"00000000000000000000000000000000",
74+
"%d", &result);
75+
EXPECT_EQ(ret_val, 1);
76+
EXPECT_EQ(result, int(LIBC_NAMESPACE::cpp::numeric_limits<intmax_t>::max()));
77+
78+
ret_val = call_vsscanf("Not an integer", "%d", &result);
79+
EXPECT_EQ(ret_val, 0);
80+
}
81+
82+
TEST(LlvmLibcVSScanfTest, IntConvLengthModifier) {
83+
int ret_val;
84+
uintmax_t max_result = 0;
85+
int int_result = 0;
86+
char char_result = 0;
87+
88+
ret_val = call_vsscanf("123", "%ju", &max_result);
89+
EXPECT_EQ(ret_val, 1);
90+
EXPECT_EQ(max_result, uintmax_t(123));
91+
92+
// Check overflow handling
93+
ret_val =
94+
call_vsscanf("999999999999999999999999999999999999", "%ju", &max_result);
95+
EXPECT_EQ(ret_val, 1);
96+
EXPECT_EQ(max_result, LIBC_NAMESPACE::cpp::numeric_limits<uintmax_t>::max());
97+
98+
// Because this is unsigned, any out of range value should return the maximum,
99+
// even with a negative sign.
100+
ret_val =
101+
call_vsscanf("-999999999999999999999999999999999999", "%ju", &max_result);
102+
EXPECT_EQ(ret_val, 1);
103+
EXPECT_EQ(max_result, LIBC_NAMESPACE::cpp::numeric_limits<uintmax_t>::max());
104+
105+
ret_val = call_vsscanf("-18446744073709551616", "%ju", &max_result);
106+
EXPECT_EQ(ret_val, 1);
107+
EXPECT_EQ(max_result, LIBC_NAMESPACE::cpp::numeric_limits<uintmax_t>::max());
108+
109+
// But any number below the maximum should have the - sign applied.
110+
ret_val = call_vsscanf("-1", "%ju", &max_result);
111+
EXPECT_EQ(ret_val, 1);
112+
EXPECT_EQ(max_result, uintmax_t(-1));
113+
114+
ret_val = call_vsscanf("-1", "%u", &int_result);
115+
EXPECT_EQ(ret_val, 1);
116+
EXPECT_EQ(int_result, -1);
117+
118+
max_result = 0xff00ff00ff00ff00;
119+
char_result = 0x6f;
120+
121+
// Overflows for sizes larger than the maximum are handled by casting.
122+
ret_val = call_vsscanf("8589967360", "%d", &int_result);
123+
EXPECT_EQ(ret_val, 1);
124+
EXPECT_EQ(int_result, int(8589967360)); // 2^33 + 2^15
125+
126+
// Check that the adjacent values weren't touched by the overflow.
127+
ASSERT_EQ(max_result, uintmax_t(0xff00ff00ff00ff00));
128+
ASSERT_EQ(char_result, char(0x6f));
129+
130+
ret_val = call_vsscanf("-8589967360", "%d", &int_result);
131+
EXPECT_EQ(ret_val, 1);
132+
EXPECT_EQ(int_result, int(-8589967360));
133+
ASSERT_EQ(max_result, uintmax_t(0xff00ff00ff00ff00));
134+
ASSERT_EQ(char_result, char(0x6f));
135+
136+
ret_val = call_vsscanf("25", "%hhd", &char_result);
137+
EXPECT_EQ(ret_val, 1);
138+
EXPECT_EQ(char_result, char(25));
139+
}
140+
141+
TEST(LlvmLibcVSScanfTest, IntConvBaseSelection) {
142+
int ret_val;
143+
int result = 0;
144+
ret_val = call_vsscanf("0xabc123", "%i", &result);
145+
EXPECT_EQ(ret_val, 1);
146+
EXPECT_EQ(result, 0xabc123);
147+
148+
ret_val = call_vsscanf("0456", "%i", &result);
149+
EXPECT_EQ(ret_val, 1);
150+
EXPECT_EQ(result, 0456);
151+
152+
ret_val = call_vsscanf("0999", "%i", &result);
153+
EXPECT_EQ(ret_val, 1);
154+
EXPECT_EQ(result, 0);
155+
156+
ret_val = call_vsscanf("123abc456", "%i", &result);
157+
EXPECT_EQ(ret_val, 1);
158+
EXPECT_EQ(result, 123);
159+
}

0 commit comments

Comments
 (0)