[libc-commits] [libc] [libc] Add wcscasecmp and wcsncasecmp implementations. (PR #222154)
Alex Strelnikov via libc-commits
libc-commits at lists.llvm.org
Wed Sep 9 10:53:48 PDT 2026
https://github.com/strel-12 updated https://github.com/llvm/llvm-project/pull/222154
>From 2595c89896ce29110be24b8afea9ddefbe612782 Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Tue, 8 Sep 2026 21:17:49 +0000
Subject: [PATCH 1/3] Add wcscasecmp and wcsncasecmp.
---
libc/config/linux/aarch64/entrypoints.txt | 2 +
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/include/wchar.yaml | 15 ++++
libc/src/__support/wctype_utils.h | 10 +++
libc/src/wchar/CMakeLists.txt | 29 +++++++
libc/src/wchar/wcscasecmp.cpp | 38 +++++++++
libc/src/wchar/wcscasecmp.h | 26 ++++++
libc/src/wchar/wcsncasecmp.cpp | 42 ++++++++++
libc/src/wchar/wcsncasecmp.h | 27 +++++++
libc/test/src/__support/wctype_utils_test.cpp | 18 +++++
libc/test/src/wchar/CMakeLists.txt | 20 +++++
libc/test/src/wchar/wcscasecmp_test.cpp | 69 ++++++++++++++++
libc/test/src/wchar/wcsncasecmp_test.cpp | 80 +++++++++++++++++++
libc/utils/docgen/wchar.yaml | 4 +
14 files changed, 382 insertions(+)
create mode 100644 libc/src/wchar/wcscasecmp.cpp
create mode 100644 libc/src/wchar/wcscasecmp.h
create mode 100644 libc/src/wchar/wcsncasecmp.cpp
create mode 100644 libc/src/wchar/wcsncasecmp.h
create mode 100644 libc/test/src/wchar/wcscasecmp_test.cpp
create mode 100644 libc/test/src/wchar/wcsncasecmp_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index fbd9239728bac..dd668aaa0e9f9 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -454,6 +454,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.write
# wchar.h entrypoints
+ libc.src.wchar.wcscasecmp
+ libc.src.wchar.wcsncasecmp
libc.src.wchar.wcslen
libc.src.wchar.wctob
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e6aeb3abe0672..551ab19c50af5 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -494,6 +494,8 @@ set(TARGET_LIBC_ENTRYPOINTS
# wchar.h entrypoints
libc.src.wchar.btowc
+ libc.src.wchar.wcscasecmp
+ libc.src.wchar.wcsncasecmp
libc.src.wchar.wcslen
libc.src.wchar.wcsnlen
libc.src.wchar.wctob
diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml
index 1017eb827cf45..2b321f6502ff1 100644
--- a/libc/include/wchar.yaml
+++ b/libc/include/wchar.yaml
@@ -117,6 +117,21 @@ functions:
arguments:
- type: const wchar_t *
- type: const wchar_t *
+ - name: wcscasecmp
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: const wchar_t *
+ - type: const wchar_t *
+ - name: wcsncasecmp
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: const wchar_t *
+ - type: const wchar_t *
+ - type: size_t
- name: wcscoll
standards:
- stdc
diff --git a/libc/src/__support/wctype_utils.h b/libc/src/__support/wctype_utils.h
index 4d0ecd2c1c0dc..9b2eb784e257c 100644
--- a/libc/src/__support/wctype_utils.h
+++ b/libc/src/__support/wctype_utils.h
@@ -10,6 +10,7 @@
#define LLVM_LIBC_SRC___SUPPORT_WCTYPE_UTILS_H
#include "hdr/types/wchar_t.h"
+#include "src/__support/CPP/bit.h"
#include "src/__support/macros/attributes.h" // LIBC_INLINE
#include "src/__support/macros/config.h"
@@ -796,6 +797,15 @@ is_char_or_wchar(wchar_t ch, [[maybe_unused]] char, wchar_t wc_value) {
return (ch == wc_value);
}
+// Returns a three-way comparison between two wide character code points.
+LIBC_INLINE int threeway_cmp_single(wchar_t left, wchar_t right) {
+ // Valid UTF-32 code points are in [0, 0x10FFFF]. If invalid code points were
+ // treated as UB, the comparison below could instead be done in 32 bits.
+ uint64_t diff = static_cast<uint64_t>(left) - static_cast<uint64_t>(right);
+ return cpp::bit_cast<int32_t>(static_cast<uint32_t>(diff >> 32) |
+ static_cast<uint32_t>(diff & 0xFFFF));
+}
+
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt
index 722619d652500..938b7ad2e4c73 100644
--- a/libc/src/wchar/CMakeLists.txt
+++ b/libc/src/wchar/CMakeLists.txt
@@ -373,6 +373,35 @@ add_entrypoint_object(
libc.hdr.types.size_t
)
+add_entrypoint_object(
+ wcscasecmp
+ SRCS
+ wcscasecmp.cpp
+ HDRS
+ wcscasecmp.h
+ DEPENDS
+ libc.hdr.types.wchar_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.__support.wctype_utils
+)
+
+add_entrypoint_object(
+ wcsncasecmp
+ SRCS
+ wcsncasecmp.cpp
+ HDRS
+ wcsncasecmp.h
+ DEPENDS
+ libc.hdr.types.size_t
+ libc.hdr.types.wchar_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.__support.wctype_utils
+)
+
add_entrypoint_object(
wcsspn
SRCS
diff --git a/libc/src/wchar/wcscasecmp.cpp b/libc/src/wchar/wcscasecmp.cpp
new file mode 100644
index 0000000000000..29c938c549a4a
--- /dev/null
+++ b/libc/src/wchar/wcscasecmp.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the wcscasecmp function implementation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/wchar/wcscasecmp.h"
+
+#include "hdr/types/wchar_t.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/wctype_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, wcscasecmp,
+ (const wchar_t *left, const wchar_t *right)) {
+ LIBC_CRASH_ON_NULLPTR(left);
+ LIBC_CRASH_ON_NULLPTR(right);
+
+ for (;; ++left, ++right) {
+ wchar_t lc = *left;
+ if (lc == L'\0' || internal::tolower(lc) != internal::tolower(*right))
+ break;
+ }
+ return internal::threeway_cmp_single(internal::tolower(*left),
+ internal::tolower(*right));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/wcscasecmp.h b/libc/src/wchar/wcscasecmp.h
new file mode 100644
index 0000000000000..51a6f1b415c56
--- /dev/null
+++ b/libc/src/wchar/wcscasecmp.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the wcscasecmp function declaration.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_WCHAR_WCSCASECMP_H
+#define LLVM_LIBC_SRC_WCHAR_WCSCASECMP_H
+
+#include "hdr/types/wchar_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int wcscasecmp(const wchar_t *left, const wchar_t *right);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_WCSCASECMP_H
diff --git a/libc/src/wchar/wcsncasecmp.cpp b/libc/src/wchar/wcsncasecmp.cpp
new file mode 100644
index 0000000000000..2fc29ff972896
--- /dev/null
+++ b/libc/src/wchar/wcsncasecmp.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the wcsncasecmp function implementation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/wchar/wcsncasecmp.h"
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/wchar_t.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/wctype_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, wcsncasecmp,
+ (const wchar_t *left, const wchar_t *right, size_t n)) {
+ LIBC_CRASH_ON_NULLPTR(left);
+ LIBC_CRASH_ON_NULLPTR(right);
+
+ if (n == 0)
+ return 0;
+
+ for (; n > 1; --n, ++left, ++right) {
+ wchar_t lc = *left;
+ if (lc == L'\0' || internal::tolower(lc) != internal::tolower(*right))
+ break;
+ }
+ return internal::threeway_cmp_single(internal::tolower(*left),
+ internal::tolower(*right));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/wcsncasecmp.h b/libc/src/wchar/wcsncasecmp.h
new file mode 100644
index 0000000000000..754ac54ec2ae2
--- /dev/null
+++ b/libc/src/wchar/wcsncasecmp.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the wcsncasecmp function declaration.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_WCHAR_WCSNCASECMP_H
+#define LLVM_LIBC_SRC_WCHAR_WCSNCASECMP_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/wchar_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int wcsncasecmp(const wchar_t *left, const wchar_t *right, size_t n);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_WCSNCASECMP_H
diff --git a/libc/test/src/__support/wctype_utils_test.cpp b/libc/test/src/__support/wctype_utils_test.cpp
index b2140ab72a3be..ae4770a7765a2 100644
--- a/libc/test/src/__support/wctype_utils_test.cpp
+++ b/libc/test/src/__support/wctype_utils_test.cpp
@@ -553,4 +553,22 @@ TEST(LlvmLibcWctypeUtilsTest, IsCntrlUtf8) {
}
}
+TEST(LlvmLibcWctypeUtilsTest, ThreewayCmpSingle) {
+ using utf8_mode::LIBC_NAMESPACE::internal::threeway_cmp_single;
+
+ EXPECT_EQ(threeway_cmp_single(L'A', L'A'), 0);
+
+ EXPECT_LT(threeway_cmp_single(wchar_t{67}, wchar_t{120}), 0);
+ EXPECT_GT(threeway_cmp_single(wchar_t{120}, wchar_t{67}), 0);
+
+ EXPECT_LT(threeway_cmp_single(wchar_t{1}, WCHAR_MAX), 0);
+ EXPECT_GT(threeway_cmp_single(WCHAR_MAX, wchar_t{1}), 0);
+
+ EXPECT_LT(threeway_cmp_single(WCHAR_MIN, wchar_t{1}), 0);
+ EXPECT_GT(threeway_cmp_single(wchar_t{1}, WCHAR_MIN), 0);
+
+ EXPECT_LT(threeway_cmp_single(WCHAR_MIN, WCHAR_MAX), 0);
+ EXPECT_GT(threeway_cmp_single(WCHAR_MAX, WCHAR_MIN), 0);
+}
+
} // namespace
diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt
index 1183d5c44d685..85ebc6e41fe0d 100644
--- a/libc/test/src/wchar/CMakeLists.txt
+++ b/libc/test/src/wchar/CMakeLists.txt
@@ -221,6 +221,26 @@ add_libc_test(
libc.src.wchar.wcsncmp
)
+add_libc_test(
+ wcscasecmp_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ wcscasecmp_test.cpp
+ DEPENDS
+ libc.src.wchar.wcscasecmp
+)
+
+add_libc_test(
+ wcsncasecmp_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ wcsncasecmp_test.cpp
+ DEPENDS
+ libc.src.wchar.wcsncasecmp
+)
+
add_libc_test(
wcscmp_test
SUITE
diff --git a/libc/test/src/wchar/wcscasecmp_test.cpp b/libc/test/src/wchar/wcscasecmp_test.cpp
new file mode 100644
index 0000000000000..4a226fb048e33
--- /dev/null
+++ b/libc/test/src/wchar/wcscasecmp_test.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains unit tests for wcscasecmp.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/wchar/wcscasecmp.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcWcscasecmpTest, EmptyStrings) {
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"", L""), 0);
+}
+
+TEST(LlvmLibcWcscasecmpTest, EmptyVsNonEmptyStrings) {
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"", L"a"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"a", L""), 0);
+}
+
+TEST(LlvmLibcWcscasecmpTest, Substrings) {
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"ab"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"a"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abc", L""), 0);
+
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"ab", L"abc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"a", L"abc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"", L"abc"), 0);
+}
+
+TEST(LlvmLibcWcscasecmpTest, MatchingStringsIgnoreCase) {
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"abc", L"abc"), 0);
+
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"Abc", L"abc"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"aBc", L"abc"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"abC", L"abc"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"ABC", L"abc"), 0);
+
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"abc", L"Abc"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"abc", L"aBc"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"abc", L"abC"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscasecmp(L"abc", L"ABC"), 0);
+}
+
+TEST(LlvmLibcWcscasecmpTest, NonMatchingStrings) {
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"Xbc", L"abc"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"aXc", L"abc"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abX", L"abc"), 0);
+
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"Xbc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"aXc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"abX"), 0);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcWcscasecmpTest, CrashOnNullPtr) {
+ EXPECT_DEATH([] { LIBC_NAMESPACE::wcscasecmp(L"hello", nullptr); },
+ WITH_SIGNAL(-1));
+ EXPECT_DEATH([] { LIBC_NAMESPACE::wcscasecmp(nullptr, L"hello"); },
+ WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
diff --git a/libc/test/src/wchar/wcsncasecmp_test.cpp b/libc/test/src/wchar/wcsncasecmp_test.cpp
new file mode 100644
index 0000000000000..61a1fae6d08fb
--- /dev/null
+++ b/libc/test/src/wchar/wcsncasecmp_test.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains unit tests for wcsncasecmp.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/limits_macros.h"
+#include "src/wchar/wcsncasecmp.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcWcsncasecmpTest, EmptyStrings) {
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"", L"", 0), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"", L"", INT_MAX), 0);
+}
+
+TEST(LlvmLibcWcsncasecmpTest, EmptyVsNonEmptyStringsUnlimited) {
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"", L"a", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"a", L"", INT_MAX), 0);
+}
+
+TEST(LlvmLibcWcsncasecmpTest, SubstringsUnlimited) {
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"ab", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"a", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"", INT_MAX), 0);
+
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"ab", L"abc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"a", L"abc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"", L"abc", INT_MAX), 0);
+}
+
+TEST(LlvmLibcWcsncasecmpTest, MatchingStringsIgnoreCaseUnlimited) {
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abc", INT_MAX), 0);
+
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"Abc", L"abc", INT_MAX), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"aBc", L"abc", INT_MAX), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abC", L"abc", INT_MAX), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"ABC", L"abc", INT_MAX), 0);
+
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"Abc", INT_MAX), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"aBc", INT_MAX), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abC", INT_MAX), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"ABC", INT_MAX), 0);
+}
+
+TEST(LlvmLibcWcsncasecmpTest, NonMatchingStringsUnlimited) {
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"Xbc", L"abc", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"aXc", L"abc", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abX", L"abc", INT_MAX), 0);
+
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"Xbc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"aXc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abX", INT_MAX), 0);
+}
+
+TEST(LlvmLibcWcsncasecmpTest, CompareAtMostStartingMatch) {
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"", 0), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"", L"def", 0), 0);
+
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"def", 0), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"ade", 1), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abd", 2), 0);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+
+TEST(LlvmLibcWcsncasecmpTest, CrashOnNullPtr) {
+ EXPECT_DEATH([] { LIBC_NAMESPACE::wcsncasecmp(L"hello", nullptr, 1); },
+ WITH_SIGNAL(-1));
+ EXPECT_DEATH([] { LIBC_NAMESPACE::wcsncasecmp(nullptr, L"hello", 1); },
+ WITH_SIGNAL(-1));
+}
+
+#endif // LIBC_ADD_NULL_CHECKS
diff --git a/libc/utils/docgen/wchar.yaml b/libc/utils/docgen/wchar.yaml
index dcc8963efdd35..85bfbe31085d1 100644
--- a/libc/utils/docgen/wchar.yaml
+++ b/libc/utils/docgen/wchar.yaml
@@ -57,6 +57,10 @@ functions:
c-definition: 7.31.4.6.2
wcscmp:
c-definition: 7.31.4.5.2
+ wcscasecmp:
+ in-latest-posix: ''
+ wcsncasecmp:
+ in-latest-posix: ''
wcscoll:
c-definition: 7.31.4.5.3
wcscpy:
>From 9972c084a3ee8688f9e0968a7fe6c53f56c5c08c Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Wed, 9 Sep 2026 13:47:38 +0000
Subject: [PATCH 2/3] Switch if-based threeway comparison.
---
libc/src/__support/wctype_utils.h | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/libc/src/__support/wctype_utils.h b/libc/src/__support/wctype_utils.h
index 9b2eb784e257c..633c9139f7369 100644
--- a/libc/src/__support/wctype_utils.h
+++ b/libc/src/__support/wctype_utils.h
@@ -800,10 +800,12 @@ is_char_or_wchar(wchar_t ch, [[maybe_unused]] char, wchar_t wc_value) {
// Returns a three-way comparison between two wide character code points.
LIBC_INLINE int threeway_cmp_single(wchar_t left, wchar_t right) {
// Valid UTF-32 code points are in [0, 0x10FFFF]. If invalid code points were
- // treated as UB, the comparison below could instead be done in 32 bits.
- uint64_t diff = static_cast<uint64_t>(left) - static_cast<uint64_t>(right);
- return cpp::bit_cast<int32_t>(static_cast<uint32_t>(diff >> 32) |
- static_cast<uint32_t>(diff & 0xFFFF));
+ // treated as UB, the comparison below could done with 32-bit arithmetic.
+ if (left == right)
+ return 0;
+ if (left < right)
+ return -1;
+ return 1;
}
} // namespace internal
>From f37c34651701e2e967ba87cc2ea73a1483624237 Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Wed, 9 Sep 2026 17:53:25 +0000
Subject: [PATCH 3/3] Add some non-ascii characters
---
libc/test/src/wchar/wcscasecmp_test.cpp | 24 +++++++++---------
libc/test/src/wchar/wcsncasecmp_test.cpp | 32 ++++++++++++------------
2 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/libc/test/src/wchar/wcscasecmp_test.cpp b/libc/test/src/wchar/wcscasecmp_test.cpp
index 4a226fb048e33..e5093edeefc83 100644
--- a/libc/test/src/wchar/wcscasecmp_test.cpp
+++ b/libc/test/src/wchar/wcscasecmp_test.cpp
@@ -24,13 +24,13 @@ TEST(LlvmLibcWcscasecmpTest, EmptyVsNonEmptyStrings) {
}
TEST(LlvmLibcWcscasecmpTest, Substrings) {
- EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"ab"), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"a"), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abc", L""), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"aβc", L"aβ"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"aβc", L"a"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"aβc", L""), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"ab", L"abc"), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"a", L"abc"), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"", L"abc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"aβ", L"aβc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"a", L"aβc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"", L"aβc"), 0);
}
TEST(LlvmLibcWcscasecmpTest, MatchingStringsIgnoreCase) {
@@ -48,13 +48,13 @@ TEST(LlvmLibcWcscasecmpTest, MatchingStringsIgnoreCase) {
}
TEST(LlvmLibcWcscasecmpTest, NonMatchingStrings) {
- EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"Xbc", L"abc"), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"aXc", L"abc"), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abX", L"abc"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"Δbc", L"abc"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"aΔc", L"abc"), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcscasecmp(L"abΔ", L"abc"), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"Xbc"), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"aXc"), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"abX"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"Δbc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"aΔc"), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcscasecmp(L"abc", L"abΔ"), 0);
}
#if defined(LIBC_ADD_NULL_CHECKS)
diff --git a/libc/test/src/wchar/wcsncasecmp_test.cpp b/libc/test/src/wchar/wcsncasecmp_test.cpp
index 61a1fae6d08fb..1989aabd61fa7 100644
--- a/libc/test/src/wchar/wcsncasecmp_test.cpp
+++ b/libc/test/src/wchar/wcsncasecmp_test.cpp
@@ -26,13 +26,13 @@ TEST(LlvmLibcWcsncasecmpTest, EmptyVsNonEmptyStringsUnlimited) {
}
TEST(LlvmLibcWcsncasecmpTest, SubstringsUnlimited) {
- EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"ab", INT_MAX), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"a", INT_MAX), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"aβc", L"aβ", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"aβc", L"a", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"aβc", L"", INT_MAX), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"ab", L"abc", INT_MAX), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"a", L"abc", INT_MAX), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"", L"abc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"aβ", L"aβc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"a", L"aβc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"", L"aβc", INT_MAX), 0);
}
TEST(LlvmLibcWcsncasecmpTest, MatchingStringsIgnoreCaseUnlimited) {
@@ -50,22 +50,22 @@ TEST(LlvmLibcWcsncasecmpTest, MatchingStringsIgnoreCaseUnlimited) {
}
TEST(LlvmLibcWcsncasecmpTest, NonMatchingStringsUnlimited) {
- EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"Xbc", L"abc", INT_MAX), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"aXc", L"abc", INT_MAX), 0);
- EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abX", L"abc", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"Δbc", L"abc", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"aΔc", L"abc", INT_MAX), 0);
+ EXPECT_GT(LIBC_NAMESPACE::wcsncasecmp(L"abΔ", L"abc", INT_MAX), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"Xbc", INT_MAX), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"aXc", INT_MAX), 0);
- EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abX", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"Δbc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"aΔc", INT_MAX), 0);
+ EXPECT_LT(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abΔ", INT_MAX), 0);
}
TEST(LlvmLibcWcsncasecmpTest, CompareAtMostStartingMatch) {
- EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"", 0), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"βγδ", L"", 0), 0);
EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"", L"def", 0), 0);
- EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"def", 0), 0);
- EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"ade", 1), 0);
- EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"abc", L"abd", 2), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"βγδ", L"def", 0), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"βγδ", L"βde", 1), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcsncasecmp(L"βγδ", L"βγd", 2), 0);
}
#if defined(LIBC_ADD_NULL_CHECKS)
More information about the libc-commits
mailing list