diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index e1a2a26479de9..5c124cd2fd392 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -385,6 +385,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.wchar.wcscpy libc.src.wchar.wmemchr libc.src.wchar.wcpcpy + libc.src.wchar.wcpncpy # sys/uio.h entrypoints libc.src.sys.uio.writev diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 0a6a75ebbbf91..210b5028f312a 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -181,3 +181,11 @@ functions: arguments: - type: wchar_t *__restrict - type: const wchar_t *__restrict + - name: wcpncpy + standards: + - stdc + return_type: wchar_t * + arguments: + - type: wchar_t *__restrict + - type: const wchar_t *__restrict + - type: size_t diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 476cf38f4662e..cce1ab520b2cc 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -88,6 +88,18 @@ add_entrypoint_object( libc.src.string.string_utils ) +add_entrypoint_object( + wcpncpy + SRCS + wcpncpy.cpp + HDRS + wcpncpy.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.wchar_macros + libc.src.__support.macros.null_check +) + add_entrypoint_object( wcschr SRCS diff --git a/libc/src/wchar/wcpncpy.cpp b/libc/src/wchar/wcpncpy.cpp new file mode 100644 index 0000000000000..9f451b73f07cb --- /dev/null +++ b/libc/src/wchar/wcpncpy.cpp @@ -0,0 +1,36 @@ +//===-- Implementation of wcpncpy -----------------------------------------===// +// +// 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/wcpncpy.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" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(wchar_t *, wcpncpy, + (wchar_t *__restrict s1, const wchar_t *__restrict s2, + size_t n)) { + if (n) { + LIBC_CRASH_ON_NULLPTR(s1); + LIBC_CRASH_ON_NULLPTR(s2); + } + size_t i; + // Copy up until \0 is found. + for (i = 0; i < n && s2[i] != '\0'; ++i) + s1[i] = s2[i]; + // When n>strlen(src), n-strlen(src) \0 are appended. + for (; i < n; ++i) + s1[i] = L'\0'; + return s1 + i; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wcpncpy.h b/libc/src/wchar/wcpncpy.h new file mode 100644 index 0000000000000..d817239d2d0b1 --- /dev/null +++ b/libc/src/wchar/wcpncpy.h @@ -0,0 +1,23 @@ +//===-- Implementation header for wcpncpy ---------------------------------===// +// +// 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_WCPNCPY_H +#define LLVM_LIBC_SRC_WCHAR_WCPNCPY_H + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +wchar_t *wcpncpy(wchar_t *__restrict ws1, const wchar_t *__restrict ws2, + size_t n); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCPNsCPY_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index c932f3632c7ab..02edd00628fa7 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -242,3 +242,13 @@ add_libc_test( DEPENDS libc.src.wchar.wcpcpy ) + +add_libc_test( + wcpncpy_test + SUITE + libc_wchar_unittests + SRCS + wcpncpy_test.cpp + DEPENDS + libc.src.wchar.wcpncpy +) diff --git a/libc/test/src/wchar/wcpncpy_test.cpp b/libc/test/src/wchar/wcpncpy_test.cpp new file mode 100644 index 0000000000000..98738e230e32d --- /dev/null +++ b/libc/test/src/wchar/wcpncpy_test.cpp @@ -0,0 +1,84 @@ +//===-- Unittests for wcpncpy --------------------------------------------===// +// +// 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/wchar_t.h" +#include "src/wchar/wcpncpy.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWCPNCpyTest, EmptySrc) { + // Empty src should lead to empty destination. + wchar_t dest[4] = {L'a', L'b', L'c', L'\0'}; + const wchar_t *src = L""; + LIBC_NAMESPACE::wcpncpy(dest, src, 3); + ASSERT_TRUE(dest[0] == src[0]); + ASSERT_TRUE(dest[0] == L'\0'); + // Rest should also be padded with L'\0' + ASSERT_TRUE(dest[1] == L'\0'); + ASSERT_TRUE(dest[2] == L'\0'); +} + +TEST(LlvmLibcWCPNCpyTest, Untouched) { + wchar_t dest[] = {L'a', L'b'}; + const wchar_t src[] = {L'x', L'\0'}; + LIBC_NAMESPACE::wcpncpy(dest, src, 0); + ASSERT_TRUE(dest[0] == L'a'); + ASSERT_TRUE(dest[1] == L'b'); +} + +TEST(LlvmLibcWCPNCpyTest, CopyOne) { + wchar_t dest[] = {L'a', L'b'}; + const wchar_t src[] = {L'x', L'y'}; + wchar_t *res = LIBC_NAMESPACE::wcpncpy(dest, src, 1); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'b'); + ASSERT_EQ(dest + 1, res); +} + +TEST(LlvmLibcWCPNCpyTest, CopyNull) { + wchar_t dest[] = {L'a', L'b'}; + const wchar_t src[] = {L'\0', L'y'}; + wchar_t *res = LIBC_NAMESPACE::wcpncpy(dest, src, 1); + ASSERT_TRUE(dest[0] == L'\0'); + ASSERT_TRUE(dest[1] == L'b'); + ASSERT_EQ(dest + 1, res); +} + +TEST(LlvmLibcWCPNCpyTest, CopyPastSrc) { + wchar_t dest[] = {L'a', L'b'}; + const wchar_t src[] = {L'\0', L'y'}; + wchar_t *res = LIBC_NAMESPACE::wcpncpy(dest, src, 2); + ASSERT_TRUE(dest[0] == L'\0'); + ASSERT_TRUE(dest[1] == L'\0'); + ASSERT_EQ(dest + 2, res); +} + +TEST(LlvmLibcWCPNCpyTest, CopyTwoNoNull) { + wchar_t dest[] = {L'a', L'b'}; + const wchar_t src[] = {L'x', L'y'}; + wchar_t *res = LIBC_NAMESPACE::wcpncpy(dest, src, 2); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'y'); + ASSERT_EQ(dest + 2, res); +} + +TEST(LlvmLibcWCPNCpyTest, CopyTwoWithNull) { + wchar_t dest[] = {L'a', L'b'}; + const wchar_t src[] = {L'x', L'\0'}; + wchar_t *res = LIBC_NAMESPACE::wcpncpy(dest, src, 2); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'\0'); + ASSERT_EQ(dest + 2, res); +} + +#if defined(LIBC_ADD_NULL_CHECKS) && !defined(LIBC_HAS_SANITIZER) +TEST(LlvmLibcWCPNCpyTest, NullptrCrash) { + // Passing in a nullptr should crash the program. + EXPECT_DEATH([] { LIBC_NAMESPACE::wcpncpy(nullptr, nullptr, 1); }, + WITH_SIGNAL(-1)); +} +#endif // LIBC_HAS_ADDRESS_SANITIZER