From e9d571d3b6829f668e424d9dfce09f9ed7f297d9 Mon Sep 17 00:00:00 2001 From: Alex Brachet Date: Wed, 11 Jan 2023 05:38:33 +0000 Subject: [PATCH] [libc] Implement str{,n}casecmp Differential Revision: https://reviews.llvm.org/D141236 --- libc/config/baremetal/entrypoints.txt | 2 ++ libc/config/darwin/arm/entrypoints.txt | 2 ++ libc/config/gpu/entrypoints.txt | 2 ++ libc/config/linux/aarch64/entrypoints.txt | 2 ++ libc/config/linux/arm/entrypoints.txt | 2 ++ libc/config/linux/x86_64/entrypoints.txt | 2 ++ libc/config/windows/entrypoints.txt | 2 ++ libc/spec/bsd_ext.td | 20 +++++++++++++ libc/src/__support/ctype_utils.h | 6 ++++ libc/src/ctype/tolower.cpp | 6 +--- libc/src/string/CMakeLists.txt | 22 ++++++++++++++ libc/src/string/strcasecmp.cpp | 25 ++++++++++++++++ libc/src/string/strcasecmp.h | 18 ++++++++++++ libc/src/string/strncasecmp.cpp | 26 +++++++++++++++++ libc/src/string/strncasecmp.h | 20 +++++++++++++ libc/test/src/string/CMakeLists.txt | 20 +++++++++++++ libc/test/src/string/strcasecmp_test.cpp | 46 +++++++++++++++++++++++++++++ libc/test/src/string/strcmp_test.cpp | 11 +++++++ libc/test/src/string/strncasecmp_test.cpp | 48 +++++++++++++++++++++++++++++++ libc/test/src/string/strncmp_test.cpp | 11 +++++++ 20 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 libc/src/string/strcasecmp.cpp create mode 100644 libc/src/string/strcasecmp.h create mode 100644 libc/src/string/strncasecmp.cpp create mode 100644 libc/src/string/strncasecmp.h create mode 100644 libc/test/src/string/strcasecmp_test.cpp create mode 100644 libc/test/src/string/strncasecmp_test.cpp diff --git a/libc/config/baremetal/entrypoints.txt b/libc/config/baremetal/entrypoints.txt index 6efa448..db645c0 100644 --- a/libc/config/baremetal/entrypoints.txt +++ b/libc/config/baremetal/entrypoints.txt @@ -31,6 +31,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -39,6 +40,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index 4a9af69..cf29319 100644 --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -31,6 +31,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -39,6 +40,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt index 8b058df..57b314f 100644 --- a/libc/config/gpu/entrypoints.txt +++ b/libc/config/gpu/entrypoints.txt @@ -30,6 +30,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -38,6 +39,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 14e0cc9..7223975 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -40,6 +40,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -51,6 +52,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 7253652..f4228f1 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -31,6 +31,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -39,6 +40,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 138c574..2c1c938 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -40,6 +40,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -52,6 +53,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 61f28fa..01c33f9 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -31,6 +31,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.memset libc.src.string.stpcpy libc.src.string.stpncpy + libc.src.string.strcasecmp libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp @@ -39,6 +40,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strlcat libc.src.string.strlcpy libc.src.string.strlen + libc.src.string.strncasecmp libc.src.string.strncat libc.src.string.strncmp libc.src.string.strncpy diff --git a/libc/spec/bsd_ext.td b/libc/spec/bsd_ext.td index 5779ab7..9ee07ce 100644 --- a/libc/spec/bsd_ext.td +++ b/libc/spec/bsd_ext.td @@ -18,6 +18,25 @@ def BsdExtensions : StandardSpec<"BSDExtensions"> { ] >; + HeaderSpec Strings = HeaderSpec< + "strings.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "strcasecmp", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "strncasecmp", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, + ] + >; + HeaderSpec SysWait = HeaderSpec< "sys/wait.h", [], // Macros @@ -34,6 +53,7 @@ def BsdExtensions : StandardSpec<"BSDExtensions"> { let Headers = [ String, + Strings, SysWait, ]; } diff --git a/libc/src/__support/ctype_utils.h b/libc/src/__support/ctype_utils.h index f3f4b36..f673981 100644 --- a/libc/src/__support/ctype_utils.h +++ b/libc/src/__support/ctype_utils.h @@ -36,6 +36,12 @@ static constexpr bool isspace(unsigned ch) { return ch == ' ' || (ch - '\t') < 5; } +static constexpr int tolower(int ch) { + if (isupper(ch)) + return ch + ('a' - 'A'); + return ch; +} + } // namespace internal } // namespace __llvm_libc diff --git a/libc/src/ctype/tolower.cpp b/libc/src/ctype/tolower.cpp index 8fbb5aa..2c4b851 100644 --- a/libc/src/ctype/tolower.cpp +++ b/libc/src/ctype/tolower.cpp @@ -15,10 +15,6 @@ namespace __llvm_libc { // TODO: Currently restricted to default locale. // These should be extended using locale information. -LLVM_LIBC_FUNCTION(int, tolower, (int c)) { - if (internal::isupper(c)) - return c + ('a' - 'A'); - return c; -} +LLVM_LIBC_FUNCTION(int, tolower, (int c)) { return internal::tolower(c); } } // namespace __llvm_libc diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index cdce0bc..ec61735 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -116,6 +116,17 @@ add_entrypoint_object( ) add_entrypoint_object( + strcasecmp + SRCS + strcasecmp.cpp + HDRS + strcasecmp.h + DEPENDS + .memory_utils.strcmp_implementation + libc.src.__support.ctype_utils +) + +add_entrypoint_object( strcoll SRCS strcoll.cpp @@ -233,6 +244,17 @@ add_entrypoint_object( ) add_entrypoint_object( + strncasecmp + SRCS + strncasecmp.cpp + HDRS + strncasecmp.h + DEPENDS + .memory_utils.strcmp_implementation + libc.src.__support.ctype_utils +) + +add_entrypoint_object( strncpy SRCS strncpy.cpp diff --git a/libc/src/string/strcasecmp.cpp b/libc/src/string/strcasecmp.cpp new file mode 100644 index 0000000..d7a080a --- /dev/null +++ b/libc/src/string/strcasecmp.cpp @@ -0,0 +1,25 @@ +//===-- Implementation of strcasecmp --------------------------------------===// +// +// 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/string/strcasecmp.h" + +#include "src/__support/common.h" +#include "src/__support/ctype_utils.h" +#include "src/string/memory_utils/strcmp_implementations.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, strcasecmp, (const char *left, const char *right)) { + auto case_cmp = [](char a, char b) { + return __llvm_libc::internal::tolower(a) - + __llvm_libc::internal::tolower(b); + }; + return strcmp_implementation(left, right, case_cmp); +} + +} // namespace __llvm_libc diff --git a/libc/src/string/strcasecmp.h b/libc/src/string/strcasecmp.h new file mode 100644 index 0000000..ce0a4e5 --- /dev/null +++ b/libc/src/string/strcasecmp.h @@ -0,0 +1,18 @@ +//===-- Implementation header for strcasecmp --------------------*- 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_STRING_STRCASECMP_H +#define LLVM_LIBC_SRC_STRING_STRCASECMP_H + +namespace __llvm_libc { + +int strcasecmp(const char *left, const char *right); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCASECMP_H diff --git a/libc/src/string/strncasecmp.cpp b/libc/src/string/strncasecmp.cpp new file mode 100644 index 0000000..0fb2f81 --- /dev/null +++ b/libc/src/string/strncasecmp.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of strncasecmp -------------------------------------===// +// +// 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/string/strncasecmp.h" + +#include "src/__support/common.h" +#include "src/__support/ctype_utils.h" +#include "src/string/memory_utils/strcmp_implementations.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, strncasecmp, + (const char *left, const char *right, size_t n)) { + auto case_cmp = [](char a, char b) { + return __llvm_libc::internal::tolower(a) - + __llvm_libc::internal::tolower(b); + }; + return strncmp_implementation(left, right, n, case_cmp); +} + +} // namespace __llvm_libc diff --git a/libc/src/string/strncasecmp.h b/libc/src/string/strncasecmp.h new file mode 100644 index 0000000..5ef2dcd --- /dev/null +++ b/libc/src/string/strncasecmp.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strcasecmp --------------------*- 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_STRING_STRCASECMP_H +#define LLVM_LIBC_SRC_STRING_STRCASECMP_H + +#include + +namespace __llvm_libc { + +int strncasecmp(const char *left, const char *right, size_t n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCASECMP_H diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index b5de08b..99dfabb 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -105,6 +105,16 @@ add_libc_unittest( ) add_libc_unittest( + strcasecmp_test + SUITE + libc_string_unittests + SRCS + strcasecmp_test.cpp + DEPENDS + libc.src.string.strcasecmp +) + +add_libc_unittest( strcoll_test SUITE libc_string_unittests @@ -219,6 +229,16 @@ add_libc_unittest( ) add_libc_unittest( + strncasecmp_test + SUITE + libc_string_unittests + SRCS + strncasecmp_test.cpp + DEPENDS + libc.src.string.strncasecmp +) + +add_libc_unittest( strncpy_test SUITE libc_string_unittests diff --git a/libc/test/src/string/strcasecmp_test.cpp b/libc/test/src/string/strcasecmp_test.cpp new file mode 100644 index 0000000..a820010 --- /dev/null +++ b/libc/test/src/string/strcasecmp_test.cpp @@ -0,0 +1,46 @@ +//===-- Unittests for strcasecmp ------------------------------------------===// +// +// 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/string/strcasecmp.h" +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcStrCaseCmpTest, EmptyStringsShouldReturnZero) { + const char *s1 = ""; + const char *s2 = ""; + int result = __llvm_libc::strcasecmp(s1, s2); + ASSERT_EQ(result, 0); + + // Verify operands reversed. + result = __llvm_libc::strcasecmp(s2, s1); + ASSERT_EQ(result, 0); +} + +TEST(LlvmLibcStrCaseCmpTest, EmptyStringShouldNotEqualNonEmptyString) { + const char *empty = ""; + const char *s2 = "abc"; + int result = __llvm_libc::strcasecmp(empty, s2); + // This should be '\0' - 'a' = -97 + ASSERT_LT(result, 0); + + // Similar case if empty string is second argument. + const char *s3 = "123"; + result = __llvm_libc::strcasecmp(s3, empty); + // This should be '1' - '\0' = 49 + ASSERT_GT(result, 0); +} + +TEST(LlvmLibcStrCaseCmpTest, Case) { + const char *s1 = "aB"; + const char *s2 = "ab"; + int result = __llvm_libc::strcasecmp(s1, s2); + ASSERT_EQ(result, 0); + + // Verify operands reversed. + result = __llvm_libc::strcasecmp(s2, s1); + ASSERT_EQ(result, 0); +} diff --git a/libc/test/src/string/strcmp_test.cpp b/libc/test/src/string/strcmp_test.cpp index f80cdfa..9f707db 100644 --- a/libc/test/src/string/strcmp_test.cpp +++ b/libc/test/src/string/strcmp_test.cpp @@ -95,3 +95,14 @@ TEST(LlvmLibcStrCmpTest, StringArgumentSwapChangesSign) { // 'a' - 'b' = -1. ASSERT_EQ(result, -1); } + +TEST(LlvmLibcStrCmpTest, Case) { + const char *s1 = "aB"; + const char *s2 = "ab"; + int result = __llvm_libc::strcmp(s1, s2); + ASSERT_LT(result, 0); + + // Verify operands reversed. + result = __llvm_libc::strcmp(s2, s1); + ASSERT_GT(result, 0); +} diff --git a/libc/test/src/string/strncasecmp_test.cpp b/libc/test/src/string/strncasecmp_test.cpp new file mode 100644 index 0000000..0188c7f --- /dev/null +++ b/libc/test/src/string/strncasecmp_test.cpp @@ -0,0 +1,48 @@ +//===-- Unittests for strncasecmp -----------------------------------------===// +// +// 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/string/strncasecmp.h" +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcStrNCaseCmpTest, + EmptyStringsShouldReturnZeroWithSufficientLength) { + const char *s1 = ""; + const char *s2 = ""; + int result = __llvm_libc::strncasecmp(s1, s2, 1); + ASSERT_EQ(result, 0); + + // Verify operands reversed. + result = __llvm_libc::strncasecmp(s2, s1, 1); + ASSERT_EQ(result, 0); +} + +TEST(LlvmLibcStrNCaseCmpTest, + EmptyStringShouldNotEqualNonEmptyStringWithSufficientLength) { + const char *empty = ""; + const char *s2 = "abc"; + int result = __llvm_libc::strncasecmp(empty, s2, 3); + // This should be '\0' - 'a' = -97 + ASSERT_LT(result, 0); + + // Similar case if empty string is second argument. + const char *s3 = "123"; + result = __llvm_libc::strncasecmp(s3, empty, 3); + // This should be '1' - '\0' = 49 + ASSERT_GT(result, 0); +} + +TEST(LlvmLibcStrNCaseCmpTest, Case) { + const char *s1 = "aB"; + const char *s2 = "ab"; + int result = __llvm_libc::strncasecmp(s1, s2, 2); + ASSERT_EQ(result, 0); + + // Verify operands reversed. + result = __llvm_libc::strncasecmp(s2, s1, 2); + ASSERT_EQ(result, 0); +} diff --git a/libc/test/src/string/strncmp_test.cpp b/libc/test/src/string/strncmp_test.cpp index 8855e0f..52add6b 100644 --- a/libc/test/src/string/strncmp_test.cpp +++ b/libc/test/src/string/strncmp_test.cpp @@ -156,3 +156,14 @@ TEST(LlvmLibcStrNCmpTest, StringComparisonEndsOnNullByteEvenWithLongerLength) { result = __llvm_libc::strncmp(s2, s1, 7); ASSERT_EQ(result, 0); } + +TEST(LlvmLibcStrNCmpTest, Case) { + const char *s1 = "aB"; + const char *s2 = "ab"; + int result = __llvm_libc::strncmp(s1, s2, 2); + ASSERT_LT(result, 0); + + // Verify operands reversed. + result = __llvm_libc::strncmp(s2, s1, 2); + ASSERT_GT(result, 0); +} -- 2.7.4