diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index a8fa26eb3d6c4..53d7a1f384af3 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -371,6 +371,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.wchar.wmemcmp libc.src.wchar.wmempcpy libc.src.wchar.wmemcpy + libc.src.wchar.wcsncat libc.src.wchar.wcscpy # sys/uio.h entrypoints diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 5d1e63d1d8933..3a461e479f519 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -80,6 +80,14 @@ functions: - type: __restrict wchar_t * - type: const __restrict wchar_t * - type: size_t + - name: wcsncat + standards: + - stdc + return_type: wchar_t * + arguments: + - type: __restrict wchar_t * + - type: const __restrict wchar_t * + - type: size_t - name: wcscpy standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 988c362e53187..d7f973d192ea6 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -105,6 +105,18 @@ add_entrypoint_object( libc.src.__support.wctype_utils ) +add_entrypoint_object( + wcsncat + SRCS + wcsncat.cpp + HDRS + wcsncat.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.wchar_macros + libc.src.string.string_utils +) + add_entrypoint_object( wmempcpy SRCS diff --git a/libc/src/wchar/wcsncat.cpp b/libc/src/wchar/wcsncat.cpp new file mode 100644 index 0000000000000..62595b4b5418c --- /dev/null +++ b/libc/src/wchar/wcsncat.cpp @@ -0,0 +1,31 @@ +//===-- Implementation of wcsncat -----------------------------------------===// +// +// 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/wcsncat.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" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(wchar_t *, wcsncat, + (wchar_t *__restrict s1, const wchar_t *__restrict s2, + size_t n)) { + size_t size = internal::string_length(s1); + size_t i = 0; + for (; s2[i] && i < n; ++i) + s1[size + i] = s2[i]; + // Appending null character to the end of the result. + s1[size + i] = L'\0'; + return s1; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wcsncat.h b/libc/src/wchar/wcsncat.h new file mode 100644 index 0000000000000..978645e90c6ba --- /dev/null +++ b/libc/src/wchar/wcsncat.h @@ -0,0 +1,23 @@ +//===-- Implementation header for wcsncat ---------------------------------===// +// +// 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_WCSNCAT_H +#define LLVM_LIBC_SRC_WCHAR_WCSNCAT_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 *wcsncat(wchar_t *__restrict s1, const wchar_t *__restrict s2, + size_t n); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WCSNCAT_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index 4d73c43cbf8e1..7b2555e74e491 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -105,6 +105,16 @@ add_libc_test( libc.src.wchar.wmemcpy ) +add_libc_test( + wcsncat_test + SUITE + libc_wchar_unittests + SRCS + wcsncat_test.cpp + DEPENDS + libc.src.wchar.wcsncat +) + add_libc_test( wcscpy_test SUITE diff --git a/libc/test/src/wchar/wcsncat_test.cpp b/libc/test/src/wchar/wcsncat_test.cpp new file mode 100644 index 0000000000000..47359f88cec9e --- /dev/null +++ b/libc/test/src/wchar/wcsncat_test.cpp @@ -0,0 +1,82 @@ +//===-- Unittests for wcscat ---------------------------------------------===// +// +// 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/wcsncat.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWCSNCatTest, EmptyDest) { + wchar_t dest[4] = {L'\0'}; + const wchar_t *src = L"abc"; + + // Start by copying nothing + LIBC_NAMESPACE::wcsncat(dest, src, 0); + ASSERT_TRUE(dest[0] == L'\0'); + + // Copying part of it. + LIBC_NAMESPACE::wcsncat(dest, src, 1); + ASSERT_TRUE(dest[0] == L'a'); + ASSERT_TRUE(dest[1] == L'\0'); + + // Resetting for the last test. + dest[0] = '\0'; + + // Copying all of it. + LIBC_NAMESPACE::wcsncat(dest, src, 3); + ASSERT_TRUE(dest[0] == L'a'); + ASSERT_TRUE(dest[1] == L'b'); + ASSERT_TRUE(dest[2] == L'c'); + ASSERT_TRUE(dest[3] == L'\0'); +} + +TEST(LlvmLibcWCSNCatTest, NonEmptyDest) { + wchar_t dest[7] = {L'x', L'y', L'z', L'\0'}; + const wchar_t *src = L"abc"; + + // Adding on only part of the string + LIBC_NAMESPACE::wcsncat(dest, src, 1); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'y'); + ASSERT_TRUE(dest[2] == L'z'); + ASSERT_TRUE(dest[3] == L'a'); + ASSERT_TRUE(dest[4] == L'\0'); + + // Copying more without resetting + LIBC_NAMESPACE::wcsncat(dest, src, 2); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'y'); + ASSERT_TRUE(dest[2] == L'z'); + ASSERT_TRUE(dest[3] == L'a'); + ASSERT_TRUE(dest[4] == L'a'); + ASSERT_TRUE(dest[5] == L'b'); + ASSERT_TRUE(dest[6] == L'\0'); + + // Setting end marker to make sure it overwrites properly. + dest[3] = L'\0'; + + // Copying all of it. + LIBC_NAMESPACE::wcsncat(dest, src, 3); + ASSERT_TRUE(dest[0] == L'x'); + ASSERT_TRUE(dest[1] == L'y'); + ASSERT_TRUE(dest[2] == L'z'); + ASSERT_TRUE(dest[3] == L'a'); + ASSERT_TRUE(dest[4] == L'b'); + ASSERT_TRUE(dest[5] == L'c'); + ASSERT_TRUE(dest[6] == L'\0'); + + // Check that copying still works when count > src length. + dest[0] = L'\0'; + // And that it doesn't write beyond what is necessary. + dest[4] = L'Z'; + LIBC_NAMESPACE::wcsncat(dest, src, 4); + ASSERT_TRUE(dest[0] == L'a'); + ASSERT_TRUE(dest[1] == L'b'); + ASSERT_TRUE(dest[2] == L'c'); + ASSERT_TRUE(dest[3] == L'\0'); + ASSERT_TRUE(dest[4] == L'Z'); +}