[libc-commits] [libc] e9d571d - [libc] Implement str{,n}casecmp

Alex Brachet via libc-commits libc-commits at lists.llvm.org
Tue Jan 10 21:39:05 PST 2023


Author: Alex Brachet
Date: 2023-01-11T05:38:33Z
New Revision: e9d571d3b6829f668e424d9dfce09f9ed7f297d9

URL: https://github.com/llvm/llvm-project/commit/e9d571d3b6829f668e424d9dfce09f9ed7f297d9
DIFF: https://github.com/llvm/llvm-project/commit/e9d571d3b6829f668e424d9dfce09f9ed7f297d9.diff

LOG: [libc] Implement str{,n}casecmp

Differential Revision: https://reviews.llvm.org/D141236

Added: 
    libc/src/string/strcasecmp.cpp
    libc/src/string/strcasecmp.h
    libc/src/string/strncasecmp.cpp
    libc/src/string/strncasecmp.h
    libc/test/src/string/strcasecmp_test.cpp
    libc/test/src/string/strncasecmp_test.cpp

Modified: 
    libc/config/baremetal/entrypoints.txt
    libc/config/darwin/arm/entrypoints.txt
    libc/config/gpu/entrypoints.txt
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/arm/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/config/windows/entrypoints.txt
    libc/spec/bsd_ext.td
    libc/src/__support/ctype_utils.h
    libc/src/ctype/tolower.cpp
    libc/src/string/CMakeLists.txt
    libc/test/src/string/CMakeLists.txt
    libc/test/src/string/strcmp_test.cpp
    libc/test/src/string/strncmp_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/baremetal/entrypoints.txt b/libc/config/baremetal/entrypoints.txt
index 6efa4484b8ab4..db645c06fe265 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 4a9af696f728b..cf2931963cfa8 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 8b058df246c6a..57b314f3d231f 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 14e0cc934bb26..7223975eb8355 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 725365231af5c..f4228f1201460 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 138c574cf4890..2c1c938c5fff5 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 61f28fae5bbc1..01c33f9d68d46 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 5779ab79c4f1d..9ee07ce4e182f 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<IntType>,
+            [ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
+        >,
+        FunctionSpec<
+            "strncasecmp",
+            RetValSpec<IntType>,
+            [ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>, ArgSpec<SizeTType>]
+        >,
+      ]
+  >;
+
   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 f3f4b366d91d9..f673981224439 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 8fbb5aaded48a..2c4b851a615e0 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 cdce0bc69ec30..ec61735f41542 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -115,6 +115,17 @@ add_entrypoint_object(
     .memory_utils.strcmp_implementation
 )
 
+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
@@ -232,6 +243,17 @@ add_entrypoint_object(
     .memory_utils.strcmp_implementation
 )
 
+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

diff  --git a/libc/src/string/strcasecmp.cpp b/libc/src/string/strcasecmp.cpp
new file mode 100644
index 0000000000000..d7a080aec949e
--- /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 0000000000000..ce0a4e5b613cc
--- /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 0000000000000..0fb2f81b17fe1
--- /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 0000000000000..5ef2dcd4cd120
--- /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 <stddef.h>
+
+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 b5de08b2fbb03..99dfabbd93a39 100644
--- a/libc/test/src/string/CMakeLists.txt
+++ b/libc/test/src/string/CMakeLists.txt
@@ -104,6 +104,16 @@ add_libc_unittest(
     libc.src.string.strcmp
 )
 
+add_libc_unittest(
+  strcasecmp_test
+  SUITE
+    libc_string_unittests
+  SRCS
+    strcasecmp_test.cpp
+  DEPENDS
+    libc.src.string.strcasecmp
+)
+
 add_libc_unittest(
   strcoll_test
   SUITE
@@ -218,6 +228,16 @@ add_libc_unittest(
     libc.src.string.strncmp
 )
 
+add_libc_unittest(
+  strncasecmp_test
+  SUITE
+    libc_string_unittests
+  SRCS
+    strncasecmp_test.cpp
+  DEPENDS
+    libc.src.string.strncasecmp
+)
+
 add_libc_unittest(
   strncpy_test
   SUITE

diff  --git a/libc/test/src/string/strcasecmp_test.cpp b/libc/test/src/string/strcasecmp_test.cpp
new file mode 100644
index 0000000000000..a8200104d22c2
--- /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 f80cdfa3d44d6..9f707dbf1581b 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 0000000000000..0188c7fca74e9
--- /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 8855e0f59db0a..52add6b516e8d 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);
+}


        


More information about the libc-commits mailing list