diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -29,6 +29,7 @@ libc.src.string.strcpy libc.src.string.strcspn libc.src.string.strlen + libc.src.string.strncpy libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -47,6 +47,7 @@ libc.src.string.strcpy libc.src.string.strcspn libc.src.string.strlen + libc.src.string.strncpy libc.src.string.strnlen libc.src.string.strpbrk libc.src.string.strrchr diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -74,6 +74,14 @@ strstr.h ) +add_entrypoint_object( + strncpy + SRCS + strncpy.cpp + HDRS + strncpy.h +) + add_entrypoint_object( strnlen SRCS diff --git a/libc/src/string/strncpy.h b/libc/src/string/strncpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strncpy.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strncpy -----------------------*- C++ -*-===// +// +// 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_STRING_STRNCPY_H +#define LLVM_LIBC_SRC_STRING_STRNCPY_H + +#include + +namespace __llvm_libc { + +char *strncpy(char *__restrict dest, const char *__restrict src, size_t n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRNCPY_H diff --git a/libc/src/string/strncpy.cpp b/libc/src/string/strncpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strncpy.cpp @@ -0,0 +1,28 @@ +//===-- Implementation of strncpy -----------------------------------------===// +// +// 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/string/strncpy.h" + +#include "src/__support/common.h" +#include // For size_t. + +namespace __llvm_libc { + +char *LLVM_LIBC_ENTRYPOINT(strncpy)(char *__restrict dest, + const char *__restrict src, size_t n) { + size_t i = 0; + // Copy up until \0 is found. + for (; i < n && src[i] != '\0'; ++i) + dest[i] = src[i]; + // When n>strlen(src), n-strlen(src) \0 are appended. + for (; i < n; ++i) + dest[i] = '\0'; + return dest; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -72,6 +72,16 @@ libc.src.string.strstr ) +add_libc_unittest( + strncpy_test + SUITE + libc_string_unittests + SRCS + strncpy_test.cpp + DEPENDS + libc.src.string.strncpy +) + add_libc_unittest( strnlen_test SUITE diff --git a/libc/test/src/string/strncpy_test.cpp b/libc/test/src/string/strncpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strncpy_test.cpp @@ -0,0 +1,57 @@ +//===-- Unittests for strncpy ---------------------------------------------===// +// +// 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/string/strncpy.h" +#include "utils/CPP/ArrayRef.h" +#include "utils/UnitTest/Test.h" +#include // For size_t. + +class StrncpyTest : public __llvm_libc::testing::Test { +public: + void check_strncpy(__llvm_libc::cpp::MutableArrayRef dst, + const __llvm_libc::cpp::ArrayRef src, size_t n, + const __llvm_libc::cpp::ArrayRef expected) { + // Making sure we don't overflow buffer. + ASSERT_GE(dst.size(), n); + // Making sure strncpy returns dst. + ASSERT_EQ(__llvm_libc::strncpy(dst.data(), src.data(), n), dst.data()); + // Expected must be of the same size as dst. + ASSERT_EQ(dst.size(), expected.size()); + // Expected and dst are the same. + for (size_t i = 0; i < expected.size(); ++i) + ASSERT_EQ(expected[i], dst[i]); + } +}; + +TEST_F(StrncpyTest, Untouched) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', '\0'}; + const char expected[] = {'a', 'b'}; + check_strncpy(dst, src, 0, expected); +} + +TEST_F(StrncpyTest, CopyOne) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'b'}; // no \0 is appended + check_strncpy(dst, src, 1, expected); +} + +TEST_F(StrncpyTest, CopyNull) { + char dst[] = {'a', 'b'}; + const char src[] = {'\0', 'y'}; + const char expected[] = {'\0', 'b'}; + check_strncpy(dst, src, 1, expected); +} + +TEST_F(StrncpyTest, CopyPastSrc) { + char dst[] = {'a', 'b'}; + const char src[] = {'\0', 'y'}; + const char expected[] = {'\0', '\0'}; + check_strncpy(dst, src, 2, expected); +}