From 21f31c6f7460902a33783ca742f9bd36bf7400e8 Mon Sep 17 00:00:00 2001 From: Sriya Pratipati Date: Tue, 24 Jun 2025 22:36:46 +0000 Subject: [PATCH 1/3] [libc] Implemented wcsnlen Implemented wcsnlen and tests for the function. --- libc/config/linux/x86_64/entrypoints.txt | 1 + libc/include/wchar.yaml | 7 ++++ libc/src/wchar/CMakeLists.txt | 12 +++++++ libc/src/wchar/wcsnlen.cpp | 24 +++++++++++++ libc/src/wchar/wcsnlen.h | 22 ++++++++++++ libc/test/src/wchar/CMakeLists.txt | 12 +++++++ libc/test/src/wchar/wcsnlen_test.cpp | 46 ++++++++++++++++++++++++ 7 files changed, 124 insertions(+) create mode 100644 libc/src/wchar/wcsnlen.cpp create mode 100644 libc/src/wchar/wcsnlen.h create mode 100644 libc/test/src/wchar/wcsnlen_test.cpp diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index c8a6d6e648af9..01bd6ce195bca 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 # wchar.h entrypoints libc.src.wchar.btowc libc.src.wchar.wcslen + libc.src.wchar.wcsnlen libc.src.wchar.wctob libc.src.wchar.wmemmove libc.src.wchar.wmemset diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 98cb3bdaf0ac9..a9f785a45b9a2 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -17,6 +17,13 @@ functions: return_type: size_t arguments: - type: const wchar_t * + - name: wcsnlen + standards: + - stdc + return_type: size_t + arguments: + - type: const wchar_t * + - type: size_t - name: wctob standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index f390785e5817b..3be70b0422377 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -10,6 +10,18 @@ add_entrypoint_object( libc.src.string.string_utils ) +add_entrypoint_object( + wcsnlen + SRCS + wcsnlen.cpp + HDRS + wcsnlen.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.wchar_t + libc.src.string.string_utils +) + add_entrypoint_object( wctob SRCS diff --git a/libc/src/wchar/wcsnlen.cpp b/libc/src/wchar/wcsnlen.cpp new file mode 100644 index 0000000000000..d54301b210b6d --- /dev/null +++ b/libc/src/wchar/wcsnlen.cpp @@ -0,0 +1,24 @@ +//===-- Implementation of wcsnlen -----------------------------------------===// +// +// 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/wcsnlen.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/string/string_utils.h" // string_length_trivial + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(size_t, wcsnlen, (const wchar_t *src, size_t maxlen)) { + size_t temp = internal::string_length(src); + return temp > maxlen ? maxlen : temp; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wcsnlen.h b/libc/src/wchar/wcsnlen.h new file mode 100644 index 0000000000000..5a4c92d368a54 --- /dev/null +++ b/libc/src/wchar/wcsnlen.h @@ -0,0 +1,22 @@ +//===-- Implementation header for wcsnlen ---------------------------------===// +// +// 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_WCSNLEN_H +#define LLVM_LIBC_SRC_WCHAR_WCSNLEN_H + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +size_t wcsnlen(const wchar_t *src, size_t maxlen); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCSNLEN_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index 48688b3bdd1f3..8dd5d8fc6399f 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -12,6 +12,18 @@ add_libc_test( libc.src.wchar.wcslen ) +add_libc_test( + wcsnlen_test + SUITE + libc_wchar_unittests + SRCS + wcsnlen_test.cpp + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.wchar_t + libc.src.wchar.wcsnlen +) + add_libc_test( btowc_test SUITE diff --git a/libc/test/src/wchar/wcsnlen_test.cpp b/libc/test/src/wchar/wcsnlen_test.cpp new file mode 100644 index 0000000000000..771df44d81641 --- /dev/null +++ b/libc/test/src/wchar/wcsnlen_test.cpp @@ -0,0 +1,46 @@ +//===-- Unittests for wcsnlen ---------------------------------------------===// +// +// 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 "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/wchar/wcsnlen.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWCSNLenTest, EmptyString) { + ASSERT_EQ(static_cast(0), LIBC_NAMESPACE::wcsnlen(L"", 0)); + // If N is greater than string length, this should still return 0. + ASSERT_EQ(static_cast(0), LIBC_NAMESPACE::wcsnlen(L"", 1)); +} + +TEST(LlvmLibcWCSNLenTest, OneCharacterString) { + const wchar_t *src = L"A"; + ASSERT_EQ(static_cast(1), LIBC_NAMESPACE::wcsnlen(src, 1)); + // If N is 0, this should return 0. + ASSERT_EQ(static_cast(0), LIBC_NAMESPACE::wcsnlen(src, 0)); + // If N is greater than string length, this should still return 1. + ASSERT_EQ(static_cast(1), LIBC_NAMESPACE::wcsnlen(src, 3)); +} + +TEST(LlvmLibcWCSNLenTest, ManyCharacterString) { + const wchar_t *src = L"123456789"; + ASSERT_EQ(static_cast(9), LIBC_NAMESPACE::wcsnlen(src, 9)); + // If N is 0, this should return 0. + ASSERT_EQ(static_cast(0), LIBC_NAMESPACE::wcsnlen(src, 0)); + // If N is smaller than the string length, it should return N. + ASSERT_EQ(static_cast(3), LIBC_NAMESPACE::wcsnlen(src, 3)); + // If N is greater than string length, this should still return 9. + ASSERT_EQ(static_cast(9), LIBC_NAMESPACE::wcsnlen(src, 42)); +} + +TEST(LlvmLibcWCSNLenTest, IgnoreCharactersAfterNullTerminator) { + const wchar_t src[5] = {L'a', L'b', L'c', L'\0', L'd'}; + ASSERT_EQ(static_cast(3), LIBC_NAMESPACE::wcsnlen(src, 3)); + // This should only read up to the null terminator. + ASSERT_EQ(static_cast(3), LIBC_NAMESPACE::wcsnlen(src, 4)); + ASSERT_EQ(static_cast(3), LIBC_NAMESPACE::wcsnlen(src, 5)); +} From e5e4455322c8cd6069a084629ab6e50a0b39b623 Mon Sep 17 00:00:00 2001 From: Sriya Pratipati Date: Tue, 24 Jun 2025 22:51:42 +0000 Subject: [PATCH 2/3] fixed behavior for case with no null terminator --- libc/src/wchar/wcsnlen.cpp | 6 ++++-- libc/test/src/wchar/wcsnlen_test.cpp | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libc/src/wchar/wcsnlen.cpp b/libc/src/wchar/wcsnlen.cpp index d54301b210b6d..0fd839d6412b3 100644 --- a/libc/src/wchar/wcsnlen.cpp +++ b/libc/src/wchar/wcsnlen.cpp @@ -17,8 +17,10 @@ namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(size_t, wcsnlen, (const wchar_t *src, size_t maxlen)) { - size_t temp = internal::string_length(src); - return temp > maxlen ? maxlen : temp; + size_t i = 0; + for (; i < maxlen && src[i]; ++i) + ; + return i; } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/wchar/wcsnlen_test.cpp b/libc/test/src/wchar/wcsnlen_test.cpp index 771df44d81641..efb7198a31154 100644 --- a/libc/test/src/wchar/wcsnlen_test.cpp +++ b/libc/test/src/wchar/wcsnlen_test.cpp @@ -44,3 +44,11 @@ TEST(LlvmLibcWCSNLenTest, IgnoreCharactersAfterNullTerminator) { ASSERT_EQ(static_cast(3), LIBC_NAMESPACE::wcsnlen(src, 4)); ASSERT_EQ(static_cast(3), LIBC_NAMESPACE::wcsnlen(src, 5)); } + +TEST(LlvmLibcWCSNLenTest, NoNullTerminator) { + const wchar_t src[4] = {L'a', L'b', L'c', L'd'}; + // Should return 4 + ASSERT_EQ(static_cast(4), LIBC_NAMESPACE::wcsnlen(src, 4)); + // Should return 2 since N is smaller than string length + ASSERT_EQ(static_cast(2), LIBC_NAMESPACE::wcsnlen(src, 2)); +} From b772f17d8a6d6aafc96e4ec2e5d25117eae06e7f Mon Sep 17 00:00:00 2001 From: Sriya Pratipati Date: Thu, 26 Jun 2025 17:01:27 +0000 Subject: [PATCH 3/3] removed unnecessary include --- libc/src/wchar/CMakeLists.txt | 1 - libc/src/wchar/wcsnlen.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 3be70b0422377..6a43105421def 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -19,7 +19,6 @@ add_entrypoint_object( DEPENDS libc.hdr.types.size_t libc.hdr.types.wchar_t - libc.src.string.string_utils ) add_entrypoint_object( diff --git a/libc/src/wchar/wcsnlen.cpp b/libc/src/wchar/wcsnlen.cpp index 0fd839d6412b3..4613006ff203e 100644 --- a/libc/src/wchar/wcsnlen.cpp +++ b/libc/src/wchar/wcsnlen.cpp @@ -12,7 +12,6 @@ #include "hdr/types/wchar_t.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include "src/string/string_utils.h" // string_length_trivial namespace LIBC_NAMESPACE_DECL {