[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
This commit is contained in:
Michael Jones 2021-10-12 23:05:56 +00:00
parent 0fcda9ae57
commit db8a88fef8
10 changed files with 266 additions and 0 deletions

View File

@ -23,10 +23,12 @@ set(TARGET_LIBC_ENTRYPOINTS
# string.h entrypoints # string.h entrypoints
libc.src.string.bcmp libc.src.string.bcmp
libc.src.string.bzero libc.src.string.bzero
libc.src.string.memccpy
libc.src.string.memchr libc.src.string.memchr
libc.src.string.memcmp libc.src.string.memcmp
libc.src.string.memcpy libc.src.string.memcpy
libc.src.string.memmove libc.src.string.memmove
libc.src.string.mempcpy
libc.src.string.memrchr libc.src.string.memrchr
libc.src.string.memset libc.src.string.memset
libc.src.string.strcat libc.src.string.strcat

View File

@ -220,6 +220,21 @@ def POSIX : StandardSpec<"POSIX"> {
], ],
[], // Enumerations [], // Enumerations
[ [
FunctionSpec<
"memccpy",
RetValSpec<VoidPtr>,
[ArgSpec<VoidRestrictedPtr>,
ArgSpec<ConstVoidRestrictedPtr>,
ArgSpec<IntType>,
ArgSpec<SizeTType>]
>,
FunctionSpec<
"mempcpy",
RetValSpec<VoidPtr>,
[ArgSpec<VoidRestrictedPtr>,
ArgSpec<ConstVoidRestrictedPtr>,
ArgSpec<SizeTType>]
>,
FunctionSpec< FunctionSpec<
"strnlen", "strnlen",
RetValSpec<SizeTType>, RetValSpec<SizeTType>,

View File

@ -8,6 +8,25 @@ add_header_library(
libc.utils.CPP.standalone_cpp 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( add_entrypoint_object(
memchr memchr
SRCS SRCS

View File

@ -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

21
libc/src/string/memccpy.h Normal file
View File

@ -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

View File

@ -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

20
libc/src/string/mempcpy.h Normal file
View File

@ -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

View File

@ -2,6 +2,26 @@ add_libc_testsuite(libc_string_unittests)
add_subdirectory(memory_utils) 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( add_libc_unittest(
memchr_test memchr_test
SUITE SUITE

View File

@ -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);
}

View File

@ -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);
}