Skip to content

[libc] Implemented wcsncmp #142429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 2, 2025
Merged

[libc] Implemented wcsncmp #142429

merged 3 commits into from
Jun 2, 2025

Conversation

uzairnawaz
Copy link
Contributor

Implemented wcsncmp and added tests

@llvmbot llvmbot added the libc label Jun 2, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 2, 2025

@llvm/pr-subscribers-libc

Author: Uzair Nawaz (uzairnawaz)

Changes

Implemented wcsncmp and added tests


Full diff: https://github.com/llvm/llvm-project/pull/142429.diff

7 Files Affected:

  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/include/wchar.yaml (+8)
  • (modified) libc/src/wchar/CMakeLists.txt (+12)
  • (added) libc/src/wchar/wcsncmp.cpp (+37)
  • (added) libc/src/wchar/wcsncmp.h (+22)
  • (modified) libc/test/src/wchar/CMakeLists.txt (+10)
  • (added) libc/test/src/wchar/wcsncmp_test.cpp (+169)
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 545b9227349fe..d78fd646d9f5d 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -366,6 +366,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.wchar.wctob
     libc.src.wchar.wmemset
     libc.src.wchar.wcschr
+    libc.src.wchar.wcsncmp
     libc.src.wchar.wcspbrk
     libc.src.wchar.wcsspn
     libc.src.wchar.wmemcmp
diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml
index bfd9a10342019..f1a92268b3f72 100644
--- a/libc/include/wchar.yaml
+++ b/libc/include/wchar.yaml
@@ -42,6 +42,14 @@ functions:
     arguments: 
       - type: const wchar_t *
       - type: wchar_t
+  - name: wcsncmp
+    standards:
+      - stdc
+    return_type: int
+    arguments:
+      - type: const wchar_t *
+      - type: const wchar_t *
+      - type: size_t
   - name: wcspbrk
     standards:
       - stdc
diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt
index 9db121762348b..86065791baf98 100644
--- a/libc/src/wchar/CMakeLists.txt
+++ b/libc/src/wchar/CMakeLists.txt
@@ -68,6 +68,18 @@ add_entrypoint_object(
     libc.src.__support.wctype_utils
 )
 
+add_entrypoint_object(
+  wcsncmp
+  SRCS
+    wcsncmp.cpp
+  HDRS
+    wcsncmp.h
+  DEPENDS
+    libc.hdr.wchar_macros
+    libc.hdr.types.size_t
+    libc.src.__support.wctype_utils
+)
+
 add_entrypoint_object(
   wcsspn
   SRCS
diff --git a/libc/src/wchar/wcsncmp.cpp b/libc/src/wchar/wcsncmp.cpp
new file mode 100644
index 0000000000000..f2e052b3c9fe3
--- /dev/null
+++ b/libc/src/wchar/wcsncmp.cpp
@@ -0,0 +1,37 @@
+//===-- Implementation of wcsncmp -----------------------------------------===//
+//
+// 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/wchar/wcsncmp.h"
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/wchar_t.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, wcsncmp,
+                   (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;
+
+  auto comp = [](wchar_t l, wchar_t r) -> int { return l - r; };
+
+  for (; n > 1; --n, ++left, ++right) {
+    wchar_t lc = *left;
+    if (!comp(lc, '\0') || comp(lc, *right))
+      break;
+  }
+  return comp(*reinterpret_cast<const wchar_t *>(left),
+              *reinterpret_cast<const wchar_t *>(right));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/wcsncmp.h b/libc/src/wchar/wcsncmp.h
new file mode 100644
index 0000000000000..0b4187e730e28
--- /dev/null
+++ b/libc/src/wchar/wcsncmp.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for wcsncmp ---------------------------------===//
+//
+// 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_WCHAR_WCSNCMP_H
+#define LLVM_LIBC_SRC_WCHAR_WCSNCMP_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/wchar_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int wcsncmp(const wchar_t *left, const wchar_t *right, size_t n);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_WCSNCMP_H
diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt
index 9bc230e0bddf3..41fed323e019e 100644
--- a/libc/test/src/wchar/CMakeLists.txt
+++ b/libc/test/src/wchar/CMakeLists.txt
@@ -55,6 +55,16 @@ add_libc_test(
     libc.src.wchar.wcschr
 )
 
+add_libc_test(
+  wcsncmp_test
+  SUITE
+    libc_wchar_unittests
+  SRCS
+    wcsncmp_test.cpp
+  DEPENDS
+    libc.src.wchar.wcsncmp
+)
+
 add_libc_test(
   wcspbrk_test
   SUITE
diff --git a/libc/test/src/wchar/wcsncmp_test.cpp b/libc/test/src/wchar/wcsncmp_test.cpp
new file mode 100644
index 0000000000000..28bbb52648226
--- /dev/null
+++ b/libc/test/src/wchar/wcsncmp_test.cpp
@@ -0,0 +1,169 @@
+//===-- Unittests for wcsncmp ---------------------------------------------===//
+//
+// 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/wchar/wcsncmp.h"
+#include "test/UnitTest/Test.h"
+
+// This group is just copies of the wcscmp tests, since all the same cases still
+// need to be tested.
+
+TEST(LlvmLibcWcsncmpTest, EmptyStringsShouldReturnZeroWithSufficientLength) {
+  const wchar_t *s1 = L"";
+  const wchar_t *s2 = L"";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 1);
+  ASSERT_EQ(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 1);
+  ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest,
+     EmptyStringShouldNotEqualNonEmptyStringWithSufficientLength) {
+  const wchar_t *empty = L"";
+  const wchar_t *s2 = L"abc";
+  int result = LIBC_NAMESPACE::wcsncmp(empty, s2, 3);
+  ASSERT_LT(result, 0);
+
+  // Similar case if empty string is second argument.
+  const wchar_t *s3 = L"123";
+  result = LIBC_NAMESPACE::wcsncmp(s3, empty, 3);
+  ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest, EqualStringsShouldReturnZeroWithSufficientLength) {
+  const wchar_t *s1 = L"abc";
+  const wchar_t *s2 = L"abc";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 3);
+  ASSERT_EQ(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 3);
+  ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest,
+     ShouldReturnResultOfFirstDifferenceWithSufficientLength) {
+  const wchar_t *s1 = L"___B42__";
+  const wchar_t *s2 = L"___C55__";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 8);
+  ASSERT_LT(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 8);
+  ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest,
+     CapitalizedLetterShouldNotBeEqualWithSufficientLength) {
+  const wchar_t *s1 = L"abcd";
+  const wchar_t *s2 = L"abCd";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 4);
+  ASSERT_GT(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 4);
+  ASSERT_LT(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest,
+     UnequalLengthStringsShouldNotReturnZeroWithSufficientLength) {
+  const wchar_t *s1 = L"abc";
+  const wchar_t *s2 = L"abcd";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 4);
+  ASSERT_LT(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 4);
+  ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest, StringArgumentSwapChangesSignWithSufficientLength) {
+  const wchar_t *a = L"a";
+  const wchar_t *b = L"b";
+  int result = LIBC_NAMESPACE::wcsncmp(b, a, 1);
+  ASSERT_GT(result, 0);
+
+  result = LIBC_NAMESPACE::wcsncmp(a, b, 1);
+  ASSERT_LT(result, 0);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS) && !defined(LIBC_HAS_SANITIZER)
+TEST(LlvmLibcWcsncmpTest, NullptrCrash) {
+  // Passing in a nullptr should crash the program.
+  EXPECT_DEATH([] { LIBC_NAMESPACE::wcsncmp(L"aaaaaaaaaaaaaa", nullptr, 3); },
+               WITH_SIGNAL(-1));
+  EXPECT_DEATH([] { LIBC_NAMESPACE::wcsncmp(nullptr, L"aaaaaaaaaaaaaa", 3); },
+               WITH_SIGNAL(-1));
+}
+#endif // LIBC_HAS_ADDRESS_SANITIZER
+
+// This group is actually testing wcsncmp functionality
+
+TEST(LlvmLibcWcsncmpTest, NonEqualStringsEqualWithLengthZero) {
+  const wchar_t *s1 = L"abc";
+  const wchar_t *s2 = L"def";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 0);
+  ASSERT_EQ(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 0);
+  ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest, NonEqualStringsNotEqualWithLengthOne) {
+  const wchar_t *s1 = L"abc";
+  const wchar_t *s2 = L"def";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 1);
+  ASSERT_LT(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 1);
+  ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest, NonEqualStringsEqualWithShorterLength) {
+  const wchar_t *s1 = L"___B42__";
+  const wchar_t *s2 = L"___C55__";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 3);
+  ASSERT_EQ(result, 0);
+
+  // This should return 'B' - 'C' = -1.
+  result = LIBC_NAMESPACE::wcsncmp(s1, s2, 4);
+  ASSERT_LT(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 3);
+  ASSERT_EQ(result, 0);
+
+  // This should return 'C' - 'B' = 1.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 4);
+  ASSERT_GT(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest, StringComparisonEndsOnNullByteEvenWithLongerLength) {
+  const wchar_t *s1 = L"abc\0def";
+  const wchar_t *s2 = L"abc\0abc";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 7);
+  ASSERT_EQ(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 7);
+  ASSERT_EQ(result, 0);
+}
+
+TEST(LlvmLibcWcsncmpTest, Case) {
+  const wchar_t *s1 = L"aB";
+  const wchar_t *s2 = L"ab";
+  int result = LIBC_NAMESPACE::wcsncmp(s1, s2, 2);
+  ASSERT_LT(result, 0);
+
+  // Verify operands reversed.
+  result = LIBC_NAMESPACE::wcsncmp(s2, s1, 2);
+  ASSERT_GT(result, 0);
+}

DEPENDS
libc.hdr.wchar_macros
libc.hdr.types.size_t
libc.src.__support.wctype_utils
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix: wctype_utils isn't used here.

Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@michaelrj-google michaelrj-google merged commit c874185 into llvm:main Jun 2, 2025
14 of 15 checks passed
sallto pushed a commit to sallto/llvm-project that referenced this pull request Jun 3, 2025
Implemented wcsncmp and added tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants