From 299b3407f2ff00168ebc3d8c7a69a7803905f701 Mon Sep 17 00:00:00 2001 From: Uzair Nawaz Date: Mon, 2 Jun 2025 16:43:47 +0000 Subject: [PATCH 1/2] implemented wcsncmp --- libc/config/linux/x86_64/entrypoints.txt | 1 + libc/include/wchar.yaml | 8 ++ libc/src/wchar/CMakeLists.txt | 12 ++ libc/src/wchar/wcsncmp.cpp | 37 +++++ libc/src/wchar/wcsncmp.h | 22 +++ libc/test/src/wchar/CMakeLists.txt | 10 ++ libc/test/src/wchar/wcsncmp_test.cpp | 169 +++++++++++++++++++++++ 7 files changed, 259 insertions(+) create mode 100644 libc/src/wchar/wcsncmp.cpp create mode 100644 libc/src/wchar/wcsncmp.h create mode 100644 libc/test/src/wchar/wcsncmp_test.cpp 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(left), + *reinterpret_cast(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); +} From 97c28b4bda07721d9418e8b469fc18b340e7d948 Mon Sep 17 00:00:00 2001 From: Uzair Nawaz Date: Mon, 2 Jun 2025 18:29:20 +0000 Subject: [PATCH 2/2] fix dependencies --- libc/src/wchar/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 86065791baf98..578ecb9009918 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -77,7 +77,6 @@ add_entrypoint_object( DEPENDS libc.hdr.wchar_macros libc.hdr.types.size_t - libc.src.__support.wctype_utils ) add_entrypoint_object(