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 @@ -31,6 +31,8 @@ libc.src.string.mempcpy libc.src.string.memrchr libc.src.string.memset + libc.src.string.stpcpy + libc.src.string.stpncpy libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -235,6 +235,19 @@ ArgSpec, ArgSpec] >, + FunctionSpec< + "stpcpy", + RetValSpec, + [ArgSpec, + ArgSpec] + >, + FunctionSpec< + "stpncpy", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, FunctionSpec< "strnlen", RetValSpec, 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 @@ -56,6 +56,27 @@ memrchr.h ) +add_entrypoint_object( + stpcpy + SRCS + stpcpy.cpp + HDRS + stpcpy.h + DEPENDS + .mempcpy + .string_utils +) + +add_entrypoint_object( + stpncpy + SRCS + stpncpy.cpp + HDRS + stpncpy.h + DEPENDS + .bzero +) + add_entrypoint_object( strcat SRCS diff --git a/libc/src/string/stpcpy.h b/libc/src/string/stpcpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/stpcpy.h @@ -0,0 +1,18 @@ +//===-- Implementation header for stpcpy ------------------------*- 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_STPCPY_H +#define LLVM_LIBC_SRC_STRING_STPCPY_H + +namespace __llvm_libc { + +char *stpcpy(char *__restrict dest, const char *__restrict src); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STPCPY_H diff --git a/libc/src/string/stpcpy.cpp b/libc/src/string/stpcpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/stpcpy.cpp @@ -0,0 +1,29 @@ +//===-- Implementation of stpcpy ------------------------------------------===// +// +// 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/stpcpy.h" +#include "src/string/mempcpy.h" +#include "src/string/string_utils.h" + +#include "src/__support/common.h" +#include "src/__support/sanitizer.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, stpcpy, + (char *__restrict dest, const char *__restrict src)) { + size_t size = internal::string_length(src) + 1; + char *result = + reinterpret_cast(__llvm_libc::mempcpy(dest, src, size)); + + if (result != nullptr) + return result - 1; + return nullptr; +} + +} // namespace __llvm_libc diff --git a/libc/src/string/stpncpy.h b/libc/src/string/stpncpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/stpncpy.h @@ -0,0 +1,20 @@ +//===-- Implementation header for stpncpy -----------------------*- 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_STPNCPY_H +#define LLVM_LIBC_SRC_STRING_STPNCPY_H + +#include + +namespace __llvm_libc { + +char *stpncpy(char *__restrict dest, const char *__restrict src, size_t n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STPNCPY_H diff --git a/libc/src/string/stpncpy.cpp b/libc/src/string/stpncpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/stpncpy.cpp @@ -0,0 +1,29 @@ +//===-- Implementation of stpncpy -----------------------------------------===// +// +// 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/stpncpy.h" +#include "src/string/bzero.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, stpncpy, + (char *__restrict dest, const char *__restrict src, + size_t n)) { + size_t i; + // Copy up until \0 is found. + for (i = 0; i < n && src[i] != '\0'; ++i) + dest[i] = src[i]; + // When n>strlen(src), n-strlen(src) \0 are appended. + if (n > i) + __llvm_libc::bzero(dest + i, n - i); + return dest + i; +} + +} // 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 @@ -42,6 +42,26 @@ libc.src.string.memrchr ) +add_libc_unittest( + stpcpy_test + SUITE + libc_string_unittests + SRCS + stpcpy_test.cpp + DEPENDS + libc.src.string.stpcpy +) + +add_libc_unittest( + stpncpy_test + SUITE + libc_string_unittests + SRCS + stpncpy_test.cpp + DEPENDS + libc.src.string.stpncpy +) + add_libc_unittest( strcat_test SUITE diff --git a/libc/test/src/string/stpcpy_test.cpp b/libc/test/src/string/stpcpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/stpcpy_test.cpp @@ -0,0 +1,45 @@ +//===-- Unittests for stpcpy ----------------------------------------------===// +// +// 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/stpcpy.h" +#include "utils/UnitTest/Test.h" + +#include "src/string/string_utils.h" + +TEST(LlvmLibcStpCpyTest, EmptySrc) { + const char *empty = ""; + size_t srcSize = __llvm_libc::internal::string_length(empty); + char dest[4] = {'a', 'b', 'c', '\0'}; + + char *result = __llvm_libc::stpcpy(dest, empty); + ASSERT_EQ(dest + srcSize, result); + ASSERT_EQ(result[0], '\0'); + ASSERT_STREQ(dest, empty); +} + +TEST(LlvmLibcStpCpyTest, EmptyDest) { + const char *abc = "abc"; + size_t srcSize = __llvm_libc::internal::string_length(abc); + char dest[4]; + + char *result = __llvm_libc::stpcpy(dest, abc); + ASSERT_EQ(dest + srcSize, result); + ASSERT_EQ(result[0], '\0'); + ASSERT_STREQ(dest, abc); +} + +TEST(LlvmLibcStpCpyTest, OffsetDest) { + const char *abc = "abc"; + size_t srcSize = __llvm_libc::internal::string_length(abc); + char dest[7] = {'x', 'y', 'z'}; + + char *result = __llvm_libc::stpcpy(dest + 3, abc); + ASSERT_EQ(dest + 3 + srcSize, result); + ASSERT_EQ(result[0], '\0'); + ASSERT_STREQ(dest, "xyzabc"); +} diff --git a/libc/test/src/string/stpncpy_test.cpp b/libc/test/src/string/stpncpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/stpncpy_test.cpp @@ -0,0 +1,73 @@ +//===-- Unittests for stpncpy ---------------------------------------------===// +// +// 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/__support/CPP/ArrayRef.h" +#include "src/string/stpncpy.h" +#include "utils/UnitTest/Test.h" +#include // For size_t. + +class LlvmLibcStpncpyTest : public __llvm_libc::testing::Test { +public: + void check_stpncpy(__llvm_libc::cpp::MutableArrayRef dst, + const __llvm_libc::cpp::ArrayRef src, size_t n, + const __llvm_libc::cpp::ArrayRef expected, + size_t expectedCopied) { + // Making sure we don't overflow buffer. + ASSERT_GE(dst.size(), n); + // Making sure stpncpy returns a pointer to the end of dst. + ASSERT_EQ(__llvm_libc::stpncpy(dst.data(), src.data(), n), + dst.data() + expectedCopied); + // 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(LlvmLibcStpncpyTest, Untouched) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', '\0'}; + const char expected[] = {'a', 'b'}; + check_stpncpy(dst, src, 0, expected, 0); +} + +TEST_F(LlvmLibcStpncpyTest, CopyOne) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'b'}; // no \0 is appended + check_stpncpy(dst, src, 1, expected, 1); +} + +TEST_F(LlvmLibcStpncpyTest, CopyNull) { + char dst[] = {'a', 'b'}; + const char src[] = {'\0', 'y'}; + const char expected[] = {'\0', 'b'}; + check_stpncpy(dst, src, 1, expected, 0); +} + +TEST_F(LlvmLibcStpncpyTest, CopyPastSrc) { + char dst[] = {'a', 'b'}; + const char src[] = {'\0', 'y'}; + const char expected[] = {'\0', '\0'}; + check_stpncpy(dst, src, 2, expected, 0); +} + +TEST_F(LlvmLibcStpncpyTest, CopyTwoNoNull) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'y'}; + check_stpncpy(dst, src, 2, expected, 2); +} + +TEST_F(LlvmLibcStpncpyTest, CopyTwoWithNull) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', '\0'}; + const char expected[] = {'x', '\0'}; + check_stpncpy(dst, src, 2, expected, 1); +} diff --git a/libc/test/src/string/strcpy_test.cpp b/libc/test/src/string/strcpy_test.cpp --- a/libc/test/src/string/strcpy_test.cpp +++ b/libc/test/src/string/strcpy_test.cpp @@ -9,6 +9,16 @@ #include "src/string/strcpy.h" #include "utils/UnitTest/Test.h" +TEST(LlvmLibcStrCpyTest, EmptySrc) { + const char *empty = ""; + char dest[4] = {'a', 'b', 'c', '\0'}; + + char *result = __llvm_libc::strcpy(dest, empty); + ASSERT_EQ(dest, result); + ASSERT_STREQ(dest, result); + ASSERT_STREQ(dest, empty); +} + TEST(LlvmLibcStrCpyTest, EmptyDest) { const char *abc = "abc"; char dest[4];