diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index f87e212eecbab..ed2994d1273c4 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -374,6 +374,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.wchar.wmemcmp libc.src.wchar.wmempcpy libc.src.wchar.wmemcpy + libc.src.wchar.wcsncpy libc.src.wchar.wcscat libc.src.wchar.wcsstr libc.src.wchar.wcsncat diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 087ace711f9a9..18b77a2711f7e 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -95,6 +95,14 @@ functions: - type: const wchar_t * - type: size_t - name: wmemcpy + standards: + - stdc + return_type: wchar_t * + arguments: + - type: __restrict wchar_t * + - type: const __restrict wchar_t * + - type: size_t + - name: wcsncpy standards: - stdc return_type: wchar_t * diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 95246ad777697..b131d2feeda06 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -137,6 +137,18 @@ add_entrypoint_object( libc.src.__support.wctype_utils ) +add_entrypoint_object( + wcsncpy + SRCS + wcsncpy.cpp + HDRS + wcsncpy.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.wchar_macros + libc.src.string.string_utils +) + add_entrypoint_object( wcscat SRCS diff --git a/libc/src/wchar/wcsncpy.cpp b/libc/src/wchar/wcsncpy.cpp new file mode 100644 index 0000000000000..e7ae9a4a0da79 --- /dev/null +++ b/libc/src/wchar/wcsncpy.cpp @@ -0,0 +1,33 @@ +//===-- Implementation of wcsncpy -----------------------------------------===// +// +// 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/wcsncpy.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/memory_utils/inline_memcpy.h" +#include "src/string/string_utils.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(wchar_t *, wcsncpy, + (wchar_t *__restrict s1, const wchar_t *__restrict s2, + size_t n)) { + size_t i = 0; + // Copy up until \0 is found. + for (; i < n && s2[i] != L'\0'; ++i) + s1[i] = s2[i]; + // When s2 is shorter than n, append \0. + for (; i < n; ++i) + s1[i] = L'\0'; + return s1; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wcsncpy.h b/libc/src/wchar/wcsncpy.h new file mode 100644 index 0000000000000..06c23f24bcde7 --- /dev/null +++ b/libc/src/wchar/wcsncpy.h @@ -0,0 +1,23 @@ +//===-- Implementation header for wcsncpy ---------------------------------===// +// +// 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_WCSNCPY_H +#define LLVM_LIBC_SRC_WCHAR_WCSNCPY_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 *wcsncpy(wchar_t *__restrict s1, const wchar_t *__restrict s2, + size_t n); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCSNCPY_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index 859d33a22617f..89c3048ae262b 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -135,6 +135,16 @@ add_libc_test( libc.src.wchar.wmemcpy ) +add_libc_test( + wcsncpy_test + SUITE + libc_wchar_unittests + SRCS + wcsncpy_test.cpp + DEPENDS + libc.src.wchar.wcsncpy +) + add_libc_test( wcscat_test SUITE diff --git a/libc/test/src/wchar/wcsncpy_test.cpp b/libc/test/src/wchar/wcsncpy_test.cpp new file mode 100644 index 0000000000000..9b5ffbe20b4a1 --- /dev/null +++ b/libc/test/src/wchar/wcsncpy_test.cpp @@ -0,0 +1,66 @@ +//===-- Unittests for wcsncpy ---------------------------------------------===// +// +// 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/wcsncpy.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWCSNCpyTest, CopyZero) { + // Dest should remain unchanged. + wchar_t dest[3] = {L'a', L'b', L'\0'}; + const wchar_t *src = L"x"; + LIBC_NAMESPACE::wcsncpy(dest, src, 0); + ASSERT_TRUE(dest[0] == L'a'); + ASSERT_TRUE(dest[1] == L'b'); + ASSERT_TRUE(dest[2] == L'\0'); +} + +TEST(LlvmLibcWCSNCpyTest, CopyFullIntoEmpty) { + // Dest should be the exact same as src. + wchar_t dest[15]; + const wchar_t *src = L"aaaaabbbbccccc"; + LIBC_NAMESPACE::wcsncpy(dest, src, 15); + for (int i = 0; i < 15; i++) + ASSERT_TRUE(dest[i] == src[i]); +} + +TEST(LlvmLibcWCSNCpyTest, CopyPartial) { + // First two characters of dest should be the first two characters of src. + wchar_t dest[] = {L'a', L'b', L'c', L'd', L'\0'}; + const wchar_t *src = L"1234"; + LIBC_NAMESPACE::wcsncpy(dest, src, 2); + ASSERT_TRUE(dest[0] == L'1'); + ASSERT_TRUE(dest[1] == L'2'); + ASSERT_TRUE(dest[2] == L'c'); + ASSERT_TRUE(dest[3] == L'd'); + ASSERT_TRUE(dest[4] == L'\0'); +} + +TEST(LlvmLibcWCSNCpyTest, CopyNullTerminator) { + // Null terminator should copy into dest. + wchar_t dest[] = {L'a', L'b', L'c', L'd', L'\0'}; + const wchar_t src[] = {L'\0', L'y'}; + LIBC_NAMESPACE::wcsncpy(dest, src, 1); + ASSERT_TRUE(dest[0] == L'\0'); + ASSERT_TRUE(dest[1] == L'b'); + ASSERT_TRUE(dest[2] == L'c'); + ASSERT_TRUE(dest[3] == L'd'); + ASSERT_TRUE(dest[4] == L'\0'); +} + +TEST(LlvmLibcWCSNCpyTest, CopyPastSrc) { + // Copying past src should fill with null terminator. + wchar_t dest[] = {L'a', L'b', L'c', L'd', L'\0'}; + const wchar_t src[] = {L'x', L'\0'}; + LIBC_NAMESPACE::wcsncpy(dest, src, 4); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'\0'); + ASSERT_TRUE(dest[2] == L'\0'); + ASSERT_TRUE(dest[3] == L'\0'); + ASSERT_TRUE(dest[4] == L'\0'); +}