diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -1,6 +1,8 @@ set(TARGET_LIBC_ENTRYPOINTS # ctype.h entrypoints + libc.src.ctype.isalnum libc.src.ctype.isalpha + libc.src.ctype.isdigit # errno.h entrypoints libc.src.errno.__errno_location diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -88,7 +88,9 @@ def CTypeAPI : PublicAPI<"ctype.h"> { let Functions = [ + "isalnum", "isalpha", + "isdigit", ]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -3,7 +3,9 @@ libc.src.assert.__assert_fail # ctype.h entrypoints + libc.src.ctype.isalnum libc.src.ctype.isalpha + libc.src.ctype.isdigit # errno.h entrypoints libc.src.errno.__errno_location diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -46,11 +46,21 @@ [], // Types [], // Enumerations [ + FunctionSpec< + "isalnum", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "isalpha", RetValSpec, [ArgSpec] >, + FunctionSpec< + "isdigit", + RetValSpec, + [ArgSpec] + >, ] >; diff --git a/libc/src/ctype/CMakeLists.txt b/libc/src/ctype/CMakeLists.txt --- a/libc/src/ctype/CMakeLists.txt +++ b/libc/src/ctype/CMakeLists.txt @@ -1,7 +1,35 @@ +add_header_library( + ctype_utils + HDRS + ctype_utils.h +) + +add_entrypoint_object( + isalnum + SRCS + isalnum.cpp + HDRS + isalnum.h + DEPENDS + .ctype_utils +) + add_entrypoint_object( isalpha SRCS isalpha.cpp HDRS isalpha.h + DEPENDS + .ctype_utils +) + +add_entrypoint_object( + isdigit + SRCS + isdigit.cpp + HDRS + isdigit.h + DEPENDS + .ctype_utils ) diff --git a/libc/src/ctype/ctype_utils.h b/libc/src/ctype/ctype_utils.h new file mode 100644 --- /dev/null +++ b/libc/src/ctype/ctype_utils.h @@ -0,0 +1,32 @@ +//===-- Collection of utils for implementing ctype functions-------*-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_CTYPE_CTYPE_UTILS_H +#define LLVM_LIBC_SRC_CTYPE_CTYPE_UTILS_H + +namespace __llvm_libc { + +// ------------------------------------------------------ +// Rationale: Since these classification functions are +// called in other functions, we will avoid the overhead +// of a function call by inlining them. +// ------------------------------------------------------ + +static inline int inlined_isdigit(int c) { + const unsigned ch = c; + return (ch - '0') < 10; +} + +static inline int inlined_isalpha(int c) { + const unsigned ch = c; + return (ch | 32) - 'a' < 26; +} + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_CTYPE_CTYPE_UTILS_H diff --git a/libc/src/ctype/isalnum.h b/libc/src/ctype/isalnum.h new file mode 100644 --- /dev/null +++ b/libc/src/ctype/isalnum.h @@ -0,0 +1,18 @@ +//===-- Implementation header for isalnum -------------------------*-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_CTYPE_ISALNUM_H +#define LLVM_LIBC_SRC_CTYPE_ISALNUM_H + +namespace __llvm_libc { + +int isalnum(int c); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_CTYPE_ISALNUM_H diff --git a/libc/src/ctype/isalpha.cpp b/libc/src/ctype/isalnum.cpp copy from libc/src/ctype/isalpha.cpp copy to libc/src/ctype/isalnum.cpp --- a/libc/src/ctype/isalpha.cpp +++ b/libc/src/ctype/isalnum.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of isalpha------------------------------------------===// +//===-- Implementation of isalnum------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "src/ctype/isalpha.h" +#include "src/ctype/isalnum.h" +#include "src/ctype/ctype_utils.h" #include "src/__support/common.h" @@ -14,9 +15,8 @@ // TODO: Currently restricted to default locale. // These should be extended using locale information. -int LLVM_LIBC_ENTRYPOINT(isalpha)(int c) { - const unsigned ch = c; - return (ch | 32) - 'a' < 26; +int LLVM_LIBC_ENTRYPOINT(isalnum)(int c) { + return inlined_isalpha(c) || inlined_isdigit(c); } } // namespace __llvm_libc diff --git a/libc/src/ctype/isalpha.cpp b/libc/src/ctype/isalpha.cpp --- a/libc/src/ctype/isalpha.cpp +++ b/libc/src/ctype/isalpha.cpp @@ -9,14 +9,12 @@ #include "src/ctype/isalpha.h" #include "src/__support/common.h" +#include "src/ctype/ctype_utils.h" namespace __llvm_libc { // TODO: Currently restricted to default locale. // These should be extended using locale information. -int LLVM_LIBC_ENTRYPOINT(isalpha)(int c) { - const unsigned ch = c; - return (ch | 32) - 'a' < 26; -} +int LLVM_LIBC_ENTRYPOINT(isalpha)(int c) { return inlined_isalpha(c); } } // namespace __llvm_libc diff --git a/libc/src/ctype/isdigit.h b/libc/src/ctype/isdigit.h new file mode 100644 --- /dev/null +++ b/libc/src/ctype/isdigit.h @@ -0,0 +1,18 @@ +//===-- Implementation header for isdigit -------------------------*-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_CTYPE_ISDIGIT_H +#define LLVM_LIBC_SRC_CTYPE_ISDIGIT_H + +namespace __llvm_libc { + +int isdigit(int c); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_CTYPE_ISDIGIT_H diff --git a/libc/src/ctype/isalpha.cpp b/libc/src/ctype/isdigit.cpp copy from libc/src/ctype/isalpha.cpp copy to libc/src/ctype/isdigit.cpp --- a/libc/src/ctype/isalpha.cpp +++ b/libc/src/ctype/isdigit.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of isalpha------------------------------------------===// +//===-- Implementation of isdigit------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,17 +6,14 @@ // //===----------------------------------------------------------------------===// -#include "src/ctype/isalpha.h" - +#include "src/ctype/isdigit.h" #include "src/__support/common.h" +#include "src/ctype/ctype_utils.h" namespace __llvm_libc { // TODO: Currently restricted to default locale. // These should be extended using locale information. -int LLVM_LIBC_ENTRYPOINT(isalpha)(int c) { - const unsigned ch = c; - return (ch | 32) - 'a' < 26; -} +int LLVM_LIBC_ENTRYPOINT(isdigit)(int c) { return inlined_isdigit(c); } } // namespace __llvm_libc diff --git a/libc/test/src/ctype/CMakeLists.txt b/libc/test/src/ctype/CMakeLists.txt --- a/libc/test/src/ctype/CMakeLists.txt +++ b/libc/test/src/ctype/CMakeLists.txt @@ -1,5 +1,15 @@ add_libc_testsuite(libc_ctype_unittests) +add_libc_unittest( + isalnum + SUITE + libc_ctype_unittests + SRCS + isalnum_test.cpp + DEPENDS + libc.src.ctype.isalnum +) + add_libc_unittest( isalpha SUITE @@ -9,3 +19,13 @@ DEPENDS libc.src.ctype.isalpha ) + +add_libc_unittest( + isdigit + SUITE + libc_ctype_unittests + SRCS + isdigit_test.cpp + DEPENDS + libc.src.ctype.isdigit +) diff --git a/libc/test/src/ctype/isalnum_test.cpp b/libc/test/src/ctype/isalnum_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/ctype/isalnum_test.cpp @@ -0,0 +1,27 @@ +//===-- Unittests for isalnum----------------------------------------------===// +// +// 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/ctype/isalnum.h" +#include "utils/UnitTest/Test.h" + +// Helper function that makes a call to isalnum a bit cleaner +// for use with testing utilities, since it explicitly requires +// a boolean value for EXPECT_TRUE and EXPECT_FALSE. +bool call_isalnum(int c) { return __llvm_libc::isalnum(c); } + +TEST(IsAlNum, DefaultLocale) { + // Loops through all characters, verifying that numbers and letters + // return true and everything else returns false. + for (int c = 0; c < 255; ++c) { + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9')) + EXPECT_TRUE(call_isalnum(c)); + else + EXPECT_FALSE(call_isalnum(c)); + } +} diff --git a/libc/test/src/ctype/isdigit_test.cpp b/libc/test/src/ctype/isdigit_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/ctype/isdigit_test.cpp @@ -0,0 +1,26 @@ +//===-- Unittests for isdigit----------------------------------------------===// +// +// 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/ctype/isdigit.h" +#include "utils/UnitTest/Test.h" + +// Helper function that makes a call to isdigit a bit cleaner +// for use with testing utilities, since it explicitly requires +// a boolean value for EXPECT_TRUE and EXPECT_FALSE. +bool call_isdigit(int c) { return __llvm_libc::isdigit(c); } + +TEST(IsDigit, DefaultLocale) { + // Loops through all characters, verifying that numbers return true + // and everything else returns false. + for (int ch = 0; ch < 255; ++ch) { + if ('0' <= ch && ch <= '9') + EXPECT_TRUE(call_isdigit(ch)); + else + EXPECT_FALSE(call_isdigit(ch)); + } +}