[libc] Implement str{,n}casecmp
authorAlex Brachet <abrachet@google.com>
Wed, 11 Jan 2023 05:38:33 +0000 (05:38 +0000)
committerAlex Brachet <abrachet@google.com>
Wed, 11 Jan 2023 05:38:33 +0000 (05:38 +0000)
Differential Revision: https://reviews.llvm.org/D141236

20 files changed:
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/src/string/strcasecmp.cpp [new file with mode: 0644]
libc/src/string/strcasecmp.h [new file with mode: 0644]
libc/src/string/strncasecmp.cpp [new file with mode: 0644]
libc/src/string/strncasecmp.h [new file with mode: 0644]
libc/test/src/string/CMakeLists.txt
libc/test/src/string/strcasecmp_test.cpp [new file with mode: 0644]
libc/test/src/string/strcmp_test.cpp
libc/test/src/string/strncasecmp_test.cpp [new file with mode: 0644]
libc/test/src/string/strncmp_test.cpp

index 6efa448..db645c0 100644 (file)
@@ -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
index 4a9af69..cf29319 100644 (file)
@@ -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
index 8b058df..57b314f 100644 (file)
@@ -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
index 14e0cc9..7223975 100644 (file)
@@ -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
index 7253652..f4228f1 100644 (file)
@@ -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
index 138c574..2c1c938 100644 (file)
@@ -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
index 61f28fa..01c33f9 100644 (file)
@@ -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
index 5779ab7..9ee07ce 100644 (file)
@@ -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,
   ];
 }
index f3f4b36..f673981 100644 (file)
@@ -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
 
index 8fbb5aa..2c4b851 100644 (file)
@@ -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
index cdce0bc..ec61735 100644 (file)
@@ -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 (file)
index 0000000..d7a080a
--- /dev/null
@@ -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 (file)
index 0000000..ce0a4e5
--- /dev/null
@@ -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 (file)
index 0000000..0fb2f81
--- /dev/null
@@ -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 (file)
index 0000000..5ef2dcd
--- /dev/null
@@ -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
index b5de08b..99dfabb 100644 (file)
@@ -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 (file)
index 0000000..a820010
--- /dev/null
@@ -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);
+}
index f80cdfa..9f707db 100644 (file)
@@ -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 (file)
index 0000000..0188c7f
--- /dev/null
@@ -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);
+}
index 8855e0f..52add6b 100644 (file)
@@ -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);
+}