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 @@ -23,10 +23,12 @@ # string.h entrypoints libc.src.string.bcmp libc.src.string.bzero + libc.src.string.memccpy libc.src.string.memchr libc.src.string.memcmp libc.src.string.memcpy libc.src.string.memmove + libc.src.string.mempcpy libc.src.string.memrchr libc.src.string.memset libc.src.string.strcat diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -220,6 +220,21 @@ ], [], // Enumerations [ + FunctionSpec< + "memccpy", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] + >, + FunctionSpec< + "mempcpy", + 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 @@ -8,6 +8,25 @@ libc.utils.CPP.standalone_cpp ) +add_entrypoint_object( + memccpy + SRCS + memccpy.cpp + HDRS + memccpy.h +) + + +add_entrypoint_object( + mempcpy + SRCS + mempcpy.cpp + HDRS + mempcpy.h + DEPENDS + libc.src.string.memcpy +) + add_entrypoint_object( memchr SRCS diff --git a/libc/src/string/memccpy.h b/libc/src/string/memccpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/memccpy.h @@ -0,0 +1,21 @@ +//===-- Implementation header for memccpy -----------------------*- 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_MEMCCPY_H +#define LLVM_LIBC_SRC_STRING_MEMCCPY_H + +#include + +namespace __llvm_libc { + +void *memccpy(void *__restrict dest, const void *__restrict src, int c, + size_t count); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_MEMCCPY_H diff --git a/libc/src/string/memccpy.cpp b/libc/src/string/memccpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/memccpy.cpp @@ -0,0 +1,35 @@ +//===-- Implementation of memccpy ----------------------------------------===// +// +// 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/memccpy.h" + +#include "src/__support/common.h" +#include // For size_t. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void *, memccpy, + (void *__restrict dest, const void *__restrict src, int c, + size_t count)) { + unsigned char end = static_cast(c); + const unsigned char *ucSrc = static_cast(src); + unsigned char *ucDest = static_cast(dest); + size_t i = 0; + // Copy up until end is found. + for (; i < count && ucSrc[i] != end; ++i) + ucDest[i] = ucSrc[i]; + // if i < count, then end must have been found, so copy end into dest and + // return the byte after. + if (i < count) { + ucDest[i] = ucSrc[i]; + return ucDest + i + 1; + } + return nullptr; +} + +} // namespace __llvm_libc diff --git a/libc/src/string/mempcpy.h b/libc/src/string/mempcpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/mempcpy.h @@ -0,0 +1,20 @@ +//===-- Implementation header for mempcpy -----------------------*- 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_MEMPCPY_H +#define LLVM_LIBC_SRC_STRING_MEMPCPY_H + +#include + +namespace __llvm_libc { + +void *mempcpy(void *__restrict dest, const void *__restrict src, size_t count); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_MEMPCPY_H diff --git a/libc/src/string/mempcpy.cpp b/libc/src/string/mempcpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/mempcpy.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of mempcpy ----------------------------------------===// +// +// 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/mempcpy.h" +#include "src/string/memcpy.h" + +#include "src/__support/common.h" +#include // For size_t. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void *, mempcpy, + (void *__restrict dest, const void *__restrict src, + size_t count)) { + void *result = __llvm_libc::memcpy(dest, src, count); + return result == nullptr + ? result + : static_cast(static_cast(result) + count); +} + +} // 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 @@ -2,6 +2,26 @@ add_subdirectory(memory_utils) +add_libc_unittest( + memccpy_test + SUITE + libc_string_unittests + SRCS + memccpy_test.cpp + DEPENDS + libc.src.string.memccpy +) + +add_libc_unittest( + mempcpy_test + SUITE + libc_string_unittests + SRCS + mempcpy_test.cpp + DEPENDS + libc.src.string.mempcpy +) + add_libc_unittest( memchr_test SUITE diff --git a/libc/test/src/string/memccpy_test.cpp b/libc/test/src/string/memccpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/memccpy_test.cpp @@ -0,0 +1,80 @@ +//===-- Unittests for memccpy ---------------------------------------------===// +// +// 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/memccpy.h" +#include "utils/CPP/ArrayRef.h" +#include "utils/UnitTest/Test.h" +#include // For size_t. + +class LlvmLibcMemccpyTest : public __llvm_libc::testing::Test { +public: + void check_memccpy(__llvm_libc::cpp::MutableArrayRef dst, + const __llvm_libc::cpp::ArrayRef src, int end, + size_t count, + const __llvm_libc::cpp::ArrayRef expected, + size_t expectedCopied, bool shouldReturnNull = false) { + // Making sure we don't overflow buffer. + ASSERT_GE(dst.size(), count); + // Making sure memccpy returns dst. + void *result = __llvm_libc::memccpy(dst.data(), src.data(), end, count); + + if (shouldReturnNull) { + ASSERT_EQ(result, static_cast(nullptr)); + } else { + ASSERT_EQ(result, static_cast(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(LlvmLibcMemccpyTest, UntouchedUnrelatedEnd) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', '\0'}; + const char expected[] = {'a', 'b'}; + check_memccpy(dst, src, 'z', 0, expected, 0, true); +} + +TEST_F(LlvmLibcMemccpyTest, UntouchedStartsWithEnd) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', '\0'}; + const char expected[] = {'a', 'b'}; + check_memccpy(dst, src, 'x', 0, expected, 0, true); +} + +TEST_F(LlvmLibcMemccpyTest, CopyOneUnrelatedEnd) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'b'}; + check_memccpy(dst, src, 'z', 1, expected, 1, true); +} + +TEST_F(LlvmLibcMemccpyTest, CopyOneStartsWithEnd) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'b'}; + check_memccpy(dst, src, 'x', 1, expected, 1); +} + +TEST_F(LlvmLibcMemccpyTest, CopyTwoUnrelatedEnd) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'y'}; + check_memccpy(dst, src, 'z', 2, expected, 2, true); +} + +TEST_F(LlvmLibcMemccpyTest, CopyTwoStartsWithEnd) { + char dst[] = {'a', 'b'}; + const char src[] = {'x', 'y'}; + const char expected[] = {'x', 'b'}; + check_memccpy(dst, src, 'x', 2, expected, 1); +} diff --git a/libc/test/src/string/mempcpy_test.cpp b/libc/test/src/string/mempcpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/mempcpy_test.cpp @@ -0,0 +1,28 @@ +//===-- Unittests for mempcpy ---------------------------------------------===// +// +// 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/mempcpy.h" +#include "utils/UnitTest/Test.h" + +// Since this function just calls out to memcpy, and memcpy has its own unit +// tests, it is assumed that memcpy works. These tests are just for the specific +// mempcpy behavior (returning the end of what was copied). +TEST(LlvmLibcMempcpyTest, Simple) { + const char *src = "12345"; + char dest[10]; + void *result = __llvm_libc::mempcpy(dest, src, 6); + ASSERT_EQ(static_cast(result), dest + 6); + ASSERT_STREQ(src, dest); +} + +TEST(LlvmLibcMempcpyTest, ZeroCount) { + const char *src = "12345"; + char dest[10]; + void *result = __llvm_libc::mempcpy(dest, src, 0); + ASSERT_EQ(static_cast(result), dest); +}