diff options
author | Uzair Nawaz <uzairnawaz@google.com> | 2025-06-02 23:05:47 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-02 16:05:47 -0700 |
commit | c8741851d1a28da9072febae5be42d9f7c113a4e (patch) | |
tree | 85f6e287b1221a8f0dabe5edd38d83679126b155 | |
parent | e3d1a33b7ef6c0f0a27ae7cc5a0b4a2572a392c4 (diff) | |
download | llvm-c8741851d1a28da9072febae5be42d9f7c113a4e.zip llvm-c8741851d1a28da9072febae5be42d9f7c113a4e.tar.gz llvm-c8741851d1a28da9072febae5be42d9f7c113a4e.tar.bz2 |
[libc] Implemented wcsncmp (#142429)
Implemented wcsncmp and added tests
-rw-r--r-- | libc/config/linux/x86_64/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/include/wchar.yaml | 8 | ||||
-rw-r--r-- | libc/src/wchar/CMakeLists.txt | 11 | ||||
-rw-r--r-- | libc/src/wchar/wcsncmp.cpp | 37 | ||||
-rw-r--r-- | libc/src/wchar/wcsncmp.h | 22 | ||||
-rw-r--r-- | libc/test/src/wchar/CMakeLists.txt | 10 | ||||
-rw-r--r-- | libc/test/src/wchar/wcsncmp_test.cpp | 169 |
7 files changed, 258 insertions, 0 deletions
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index c883cb6..a30ffbe0b 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.wcscmp libc.src.wchar.wcspbrk libc.src.wchar.wcsrchr diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 2eaefbf..29a989f 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: wcscmp standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 17b5500..be62495 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -90,6 +90,17 @@ add_entrypoint_object( ) add_entrypoint_object( + wcsncmp + SRCS + wcsncmp.cpp + HDRS + wcsncmp.h + DEPENDS + libc.hdr.wchar_macros + libc.hdr.types.size_t +) + +add_entrypoint_object( wcsspn SRCS wcsspn.cpp diff --git a/libc/src/wchar/wcsncmp.cpp b/libc/src/wchar/wcsncmp.cpp new file mode 100644 index 0000000..f2e052b --- /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 0000000..0b4187e73 --- /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 7743b0f..38ff8b8 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -56,6 +56,16 @@ add_libc_test( ) add_libc_test( + wcsncmp_test + SUITE + libc_wchar_unittests + SRCS + wcsncmp_test.cpp + DEPENDS + libc.src.wchar.wcsncmp +) + +add_libc_test( wcscmp_test SUITE libc_wchar_unittests diff --git a/libc/test/src/wchar/wcsncmp_test.cpp b/libc/test/src/wchar/wcsncmp_test.cpp new file mode 100644 index 0000000..28bbb52 --- /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); +} |