[libc] add memccpy and mempcpy

Add an implementation for memccpy and mempcpy. These functions are
posix extensions for the moment.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D111762

GitOrigin-RevId: db8a88fef87e921c331c71503f73a76337c121c9
diff --git a/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt
index 56ae31d..1f40545 100644
--- a/config/linux/x86_64/entrypoints.txt
+++ b/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/spec/posix.td b/spec/posix.td
index 32f6ef5..45d12fa 100644
--- a/spec/posix.td
+++ b/spec/posix.td
@@ -221,6 +221,21 @@
     [], // Enumerations
     [
         FunctionSpec<
+            "memccpy",
+            RetValSpec<VoidPtr>,
+            [ArgSpec<VoidRestrictedPtr>,
+             ArgSpec<ConstVoidRestrictedPtr>,
+             ArgSpec<IntType>,
+             ArgSpec<SizeTType>]
+        >,
+        FunctionSpec<
+            "mempcpy",
+            RetValSpec<VoidPtr>,
+            [ArgSpec<VoidRestrictedPtr>,
+             ArgSpec<ConstVoidRestrictedPtr>,
+             ArgSpec<SizeTType>]
+        >,
+        FunctionSpec<
             "strnlen",
              RetValSpec<SizeTType>,
              [ArgSpec<ConstCharPtr>, ArgSpec<SizeTType>]
diff --git a/src/string/CMakeLists.txt b/src/string/CMakeLists.txt
index e5d6ebc..05edd49 100644
--- a/src/string/CMakeLists.txt
+++ b/src/string/CMakeLists.txt
@@ -9,6 +9,25 @@
 )
 
 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
     memchr.cpp
diff --git a/src/string/memccpy.cpp b/src/string/memccpy.cpp
new file mode 100644
index 0000000..782beee
--- /dev/null
+++ b/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 <stddef.h> // 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<unsigned char>(c);
+  const unsigned char *ucSrc = static_cast<const unsigned char *>(src);
+  unsigned char *ucDest = static_cast<unsigned char *>(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/src/string/memccpy.h b/src/string/memccpy.h
new file mode 100644
index 0000000..38a80b1
--- /dev/null
+++ b/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 <stddef.h>
+
+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/src/string/mempcpy.cpp b/src/string/mempcpy.cpp
new file mode 100644
index 0000000..08b2b80
--- /dev/null
+++ b/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 <stddef.h> // 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<void *>(static_cast<char *>(result) + count);
+}
+
+} // namespace __llvm_libc
diff --git a/src/string/mempcpy.h b/src/string/mempcpy.h
new file mode 100644
index 0000000..6000761
--- /dev/null
+++ b/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 <stddef.h>
+
+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/test/src/string/CMakeLists.txt b/test/src/string/CMakeLists.txt
index 2d6d919..2c8d662 100644
--- a/test/src/string/CMakeLists.txt
+++ b/test/src/string/CMakeLists.txt
@@ -3,6 +3,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
     libc_string_unittests
diff --git a/test/src/string/memccpy_test.cpp b/test/src/string/memccpy_test.cpp
new file mode 100644
index 0000000..980c3a0
--- /dev/null
+++ b/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 <stddef.h> // For size_t.
+
+class LlvmLibcMemccpyTest : public __llvm_libc::testing::Test {
+public:
+  void check_memccpy(__llvm_libc::cpp::MutableArrayRef<char> dst,
+                     const __llvm_libc::cpp::ArrayRef<char> src, int end,
+                     size_t count,
+                     const __llvm_libc::cpp::ArrayRef<char> 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<void *>(nullptr));
+    } else {
+      ASSERT_EQ(result, static_cast<void *>(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/test/src/string/mempcpy_test.cpp b/test/src/string/mempcpy_test.cpp
new file mode 100644
index 0000000..9f95db5
--- /dev/null
+++ b/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<char *>(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<char *>(result), dest);
+}