Skip to content

Commit b062d63

Browse files
[libc] add strtoll function and backend
This change adds the stroll function, but most of the implementation is in the new file str_conv_utils.h since many of the other integer conversion functions are implemented through what are effectively calls to strtoll. Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D107792
1 parent 3a2ff98 commit b062d63

File tree

10 files changed

+469
-0
lines changed

10 files changed

+469
-0
lines changed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ if(LLVM_LIBC_FULL_BUILD)
158158
libc.src.stdlib.abs
159159
libc.src.stdlib.labs
160160
libc.src.stdlib.llabs
161+
libc.src.stdlib.strtoll
161162

162163
# signal.h entrypoints
163164
libc.src.signal.raise

libc/spec/spec.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def ConstVoidRestrictedPtr : ConstType<VoidRestrictedPtr>;
6262
def CharPtr : PtrType<CharType>;
6363
def ConstCharPtr : ConstType<CharPtr>;
6464
def CharRestrictedPtr : RestrictedPtrType<CharType>;
65+
def CharRestrictedPtrPtr : RestrictedPtrType<CharPtr>;
6566
def ConstCharRestrictedPtr : ConstType<CharRestrictedPtr>;
6667

6768
def OnceFlagType : NamedType<"once_flag">;

libc/spec/stdc.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ def StdC : StandardSpec<"stdc"> {
478478
FunctionSpec<"abs", RetValSpec<IntType>, [ArgSpec<IntType>]>,
479479
FunctionSpec<"labs", RetValSpec<LongType>, [ArgSpec<LongType>]>,
480480
FunctionSpec<"llabs", RetValSpec<LongLongType>, [ArgSpec<LongLongType>]>,
481+
FunctionSpec<"strtoll", RetValSpec<LongLongType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>, ArgSpec<IntType>]>,
481482
FunctionSpec<"_Exit", RetValSpec<NoReturn>, [ArgSpec<IntType>]>,
482483
]
483484
>;

libc/src/__support/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ add_header_library(
1212
ctype_utils.h
1313
)
1414

15+
add_header_library(
16+
str_conv_utils
17+
HDRS
18+
str_conv_utils.h
19+
DEPENDS
20+
.ctype_utils
21+
libc.include.errno
22+
libc.src.errno.__errno_location
23+
)
24+
1525
add_header_library(
1626
integer_operations
1727
HDRS

libc/src/__support/str_conv_utils.h

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//===-- Stdlib utils --------------------------------------------*- 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 LIBC_SRC_STDLIB_STDLIB_UTILS_H
10+
#define LIBC_SRC_STDLIB_STDLIB_UTILS_H
11+
12+
#include "src/__support/ctype_utils.h"
13+
#include <errno.h>
14+
#include <limits.h>
15+
16+
namespace __llvm_libc {
17+
namespace internal {
18+
19+
// Returns a pointer to the first character in src that is not a whitespace
20+
// character (as determined by isspace())
21+
static inline const char *first_non_whitespace(const char *__restrict src) {
22+
while (internal::isspace(*src)) {
23+
++src;
24+
}
25+
return src;
26+
}
27+
28+
static inline int b36_char_to_int(char input) {
29+
if (isdigit(input))
30+
return input - '0';
31+
if (isalpha(input))
32+
return (input | 32) + 10 - 'a';
33+
return 0;
34+
}
35+
36+
// Takes the address of the string pointer and parses the base from the start of
37+
// it. This will advance the string pointer.
38+
static inline int infer_base(const char *__restrict *__restrict src) {
39+
if (**src == '0') {
40+
++(*src);
41+
if ((**src | 32) == 'x') {
42+
++(*src);
43+
return 16;
44+
}
45+
return 8;
46+
}
47+
return 10;
48+
}
49+
50+
// Takes a pointer to a string, a pointer to a string pointer, and the base to
51+
// convert to. This function is used as the backend for all of the string to int
52+
// functions.
53+
static inline long long strtoll(const char *__restrict src,
54+
char **__restrict str_end, int base) {
55+
unsigned long long result = 0;
56+
57+
if (base < 0 || base == 1 || base > 36) {
58+
errno = EINVAL; // NOLINT
59+
return 0;
60+
}
61+
62+
src = first_non_whitespace(src);
63+
64+
char result_sign = '+';
65+
if (*src == '+' || *src == '-') {
66+
result_sign = *src;
67+
++src;
68+
}
69+
70+
if (base == 0) {
71+
base = infer_base(&src);
72+
} else if (base == 16 && *src == '0' && (*(src + 1) | 32) == 'x') {
73+
src = src + 2;
74+
}
75+
76+
unsigned long long const ABS_MAX =
77+
(result_sign == '+' ? LLONG_MAX
78+
: static_cast<unsigned long long>(LLONG_MAX) + 1);
79+
unsigned long long const ABS_MAX_DIV_BY_BASE = ABS_MAX / base;
80+
while (isalnum(*src)) {
81+
int cur_digit = b36_char_to_int(*src);
82+
if (cur_digit >= base)
83+
break;
84+
if (result > ABS_MAX_DIV_BY_BASE) {
85+
result = ABS_MAX;
86+
errno = ERANGE; // NOLINT
87+
break;
88+
}
89+
result = result * base;
90+
if (result > ABS_MAX - cur_digit) {
91+
result = ABS_MAX;
92+
errno = ERANGE; // NOLINT
93+
break;
94+
}
95+
result = result + cur_digit;
96+
97+
++src;
98+
}
99+
100+
if (str_end != nullptr)
101+
*str_end = const_cast<char *>(src);
102+
if (result_sign == '+')
103+
return result;
104+
else
105+
return -result;
106+
}
107+
108+
} // namespace internal
109+
} // namespace __llvm_libc
110+
111+
#endif // LIBC_SRC_STDLIB_STDLIB_UTILS_H

libc/src/stdlib/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,13 @@ add_entrypoint_object(
5050
DEPENDS
5151
libc.src.__support.integer_operations
5252
)
53+
54+
add_entrypoint_object(
55+
strtoll
56+
SRCS
57+
strtoll.cpp
58+
HDRS
59+
strtoll.h
60+
DEPENDS
61+
libc.src.__support.str_conv_utils
62+
)

libc/src/stdlib/strtoll.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation of strtoll -----------------------------------------===//
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/stdlib/strtoll.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/str_conv_utils.h"
12+
13+
namespace __llvm_libc {
14+
15+
LLVM_LIBC_FUNCTION(long long, strtoll,
16+
(const char *__restrict str, char **__restrict str_end,
17+
int base)) {
18+
return internal::strtoll(str, str_end, base);
19+
}
20+
21+
} // namespace __llvm_libc

libc/src/stdlib/strtoll.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- Implementation header for strtoll -----------------------*- 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_STDLIB_STRTOLL_H
10+
#define LLVM_LIBC_SRC_STDLIB_STRTOLL_H
11+
12+
namespace __llvm_libc {
13+
14+
long long strtoll(const char *__restrict str, char **__restrict str_end,
15+
int base);
16+
17+
} // namespace __llvm_libc
18+
19+
#endif // LLVM_LIBC_SRC_STDLIB_STRTOLL_H

libc/test/src/stdlib/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,15 @@ add_libc_unittest(
5454
DEPENDS
5555
libc.src.stdlib.llabs
5656
)
57+
58+
add_libc_unittest(
59+
strtoll_test
60+
SUITE
61+
libc_stdlib_unittests
62+
SRCS
63+
strtoll_test.cpp
64+
DEPENDS
65+
libc.src.stdlib.strtoll
66+
libc.include.errno
67+
libc.test.errno_setter_matcher
68+
)

0 commit comments

Comments
 (0)