diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index cfd8bf87cd7dd..b652befe3976c 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -18,6 +18,17 @@ add_entrypoint_object( .string_utils ) +add_entrypoint_object( + memmove + SRCS + memmove.cpp + HDRS + memmove.h + DEPENDS + libc.src.__support.integer_operations + libc.src.string.memcpy +) + add_entrypoint_object( memrchr SRCS @@ -324,27 +335,6 @@ else() add_memcpy(memcpy) endif() -# ------------------------------------------------------------------------------ -# memmove -# ------------------------------------------------------------------------------ - -function(add_memmove memmove_name) - add_implementation(memmove ${memmove_name} - SRCS ${MEMMOVE_SRC} - HDRS ${LIBC_SOURCE_DIR}/src/string/memmove.h - DEPENDS - .memory_utils.memory_utils - libc.src.__support.integer_operations - libc.src.string.memcpy - COMPILE_OPTIONS - -fno-builtin-memmove - ${ARGN} - ) -endfunction() - -set(MEMMOVE_SRC ${LIBC_SOURCE_DIR}/src/string/memmove.cpp) -add_memmove(memmove) - # ------------------------------------------------------------------------------ # memset # ------------------------------------------------------------------------------ diff --git a/libc/src/string/memmove.cpp b/libc/src/string/memmove.cpp index 86aa6d46ad337..ebbe4643e7da6 100644 --- a/libc/src/string/memmove.cpp +++ b/libc/src/string/memmove.cpp @@ -15,50 +15,52 @@ namespace __llvm_libc { -static inline void move_byte_backward(char *dst, const char *src, +static inline void move_byte_forward(char *dest_m, const char *src_m, + size_t count) { + for (size_t offset = 0; count; --count, ++offset) + dest_m[offset] = src_m[offset]; +} + +static inline void move_byte_backward(char *dest_m, const char *src_m, size_t count) { for (size_t offset = count - 1; count; --count, --offset) - dst[offset] = src[offset]; + dest_m[offset] = src_m[offset]; } -static void memmove_impl(char *dst, const char *src, size_t count) { +LLVM_LIBC_FUNCTION(void *, memmove, + (void *dest, const void *src, size_t count)) { + char *dest_c = reinterpret_cast(dest); + const char *src_c = reinterpret_cast(src); - // If the distance between `src` and `dst` is equal to or greater - // than count (integerAbs(src - dst) >= count), they would not overlap. - // e.g. greater equal overlapping - // [12345678] [12345678] [12345678] - // src: [_ab_____] [_ab_____] [_ab_____] - // dst: [_____yz_] [___yz___] [__yz____] + // If the distance between src_c and dest_c is equal to or greater + // than count (integerAbs(src_c - dest_c) >= count), they would not overlap. + // e.g. greater equal overlapping + // [12345678] [12345678] [12345678] + // src_c: [_ab_____] [_ab_____] [_ab_____] + // dest_c:[_____yz_] [___yz___] [__yz____] - // Call `memcpy` when `src` and `dst` do not overlap. - if (__llvm_libc::integerAbs(src - dst) >= static_cast(count)) - __llvm_libc::memcpy(dst, src, count); + // Use memcpy if src_c and dest_c do not overlap. + if (__llvm_libc::integerAbs(src_c - dest_c) >= static_cast(count)) + return __llvm_libc::memcpy(dest_c, src_c, count); // Overlap cases. - // If `dst` starts before `src` (dst < src), copy forward from beginning to - // end. If `dst` starts after `src` (dst > src), copy backward from end to - // beginning. If `dst` and `src` start at the same address (dst == src), do - // nothing. - // e.g. forward backward - // *-> <-* - // src: [___abcde_] [_abcde___] - // dst: [_abc--___] [___--cde_] - - // In `memcpy` implementation, it copies bytes forward from beginning to - // end. Otherwise, `memmove` unit tests will break. - if (dst < src) - __llvm_libc::memcpy(dst, src, count); + // If dest_c starts before src_c (dest_c < src_c), copy forward(pointer add 1) + // from beginning to end. + // If dest_c starts after src_c (dest_c > src_c), copy backward(pointer add + // -1) from end to beginning. + // If dest_c and src_c start at the same address (dest_c == src_c), + // just return dest. + // e.g. forward backward + // *--> <--* + // src_c : [___abcde_] [_abcde___] + // dest_c: [_abc--___] [___--cde_] // TODO: Optimize `move_byte_xxx(...)` functions. - if (dst > src) - move_byte_backward(dst, src, count); -} - -LLVM_LIBC_FUNCTION(void *, memmove, - (void *dst, const void *src, size_t count)) { - memmove_impl(reinterpret_cast(dst), - reinterpret_cast(src), count); - return dst; + if (dest_c < src_c) + move_byte_forward(dest_c, src_c, count); + if (dest_c > src_c) + move_byte_backward(dest_c, src_c, count); + return dest; } } // namespace __llvm_libc diff --git a/libc/test/src/string/memmove_test.cpp b/libc/test/src/string/memmove_test.cpp index 22e0b3b753ec4..e13722da76680 100644 --- a/libc/test/src/string/memmove_test.cpp +++ b/libc/test/src/string/memmove_test.cpp @@ -6,65 +6,64 @@ // //===----------------------------------------------------------------------===// +#include "src/string/memcmp.h" #include "src/string/memmove.h" #include "utils/CPP/ArrayRef.h" #include "utils/UnitTest/Test.h" class LlvmLibcMemmoveTest : public __llvm_libc::testing::Test { public: - void check_memmove(void *dst, const void *src, size_t count, - const unsigned char *str, + void check_memmove(void *dest, const void *src, size_t count, const void *str, const __llvm_libc::cpp::ArrayRef expected) { - void *result = __llvm_libc::memmove(dst, src, count); - // Making sure the pointer returned is same with `dst`. - EXPECT_EQ(result, dst); - // `expected` is designed according to `str`. - // `dst` and `src` might be part of `str`. - // Making sure `str` is same with `expected`. - for (size_t i = 0; i < expected.size(); ++i) - EXPECT_EQ(str[i], expected[i]); + void *result = __llvm_libc::memmove(dest, src, count); + // Making sure the pointer returned is same with dest. + EXPECT_EQ(result, dest); + // expected is designed according to str. + // dest and src might be part of str. + // Making sure the str is same with expected. + EXPECT_EQ(__llvm_libc::memcmp(str, expected.data(), expected.size()), 0); } }; TEST_F(LlvmLibcMemmoveTest, MoveZeroByte) { - unsigned char dst[] = {'a', 'b'}; + unsigned char dest[] = {'a', 'b'}; const unsigned char src[] = {'y', 'z'}; const unsigned char expected[] = {'a', 'b'}; - check_memmove(dst, src, 0, dst, expected); + check_memmove(dest, src, 0, dest, expected); } -TEST_F(LlvmLibcMemmoveTest, OverlapThatDstAndSrcPointToSameAddress) { +TEST_F(LlvmLibcMemmoveTest, OverlapThatDestAndSrcPointToSameAddress) { unsigned char str[] = {'a', 'b'}; const unsigned char expected[] = {'a', 'b'}; check_memmove(str, str, 1, str, expected); } -TEST_F(LlvmLibcMemmoveTest, OverlapThatDstStartsBeforeSrc) { +TEST_F(LlvmLibcMemmoveTest, OverlapThatDestStartsBeforeSrc) { // Set boundary at beginning and end for not overstepping when // copy forward or backward. unsigned char str[] = {'z', 'a', 'b', 'c', 'z'}; const unsigned char expected[] = {'z', 'b', 'c', 'c', 'z'}; - // `dst` is `&str[1]`. + // dest is &str[1]. check_memmove(&str[1], &str[2], 2, str, expected); } -TEST_F(LlvmLibcMemmoveTest, OverlapThatDstStartsAfterSrc) { +TEST_F(LlvmLibcMemmoveTest, OverlapThatDestStartsAfterSrc) { unsigned char str[] = {'z', 'a', 'b', 'c', 'z'}; const unsigned char expected[] = {'z', 'a', 'a', 'b', 'z'}; check_memmove(&str[2], &str[1], 2, str, expected); } -// e.g. `dst` follow `src`. +// e.g. dest follow src. // str: [abcdefghij] // [__src_____] -// [_____dst__] -TEST_F(LlvmLibcMemmoveTest, SrcFollowDst) { +// [_____dest_] +TEST_F(LlvmLibcMemmoveTest, SrcFollowDest) { unsigned char str[] = {'z', 'a', 'b', 'z'}; const unsigned char expected[] = {'z', 'b', 'b', 'z'}; check_memmove(&str[1], &str[2], 1, str, expected); } -TEST_F(LlvmLibcMemmoveTest, DstFollowSrc) { +TEST_F(LlvmLibcMemmoveTest, DestFollowSrc) { unsigned char str[] = {'z', 'a', 'b', 'z'}; const unsigned char expected[] = {'z', 'a', 'a', 'z'}; check_memmove(&str[2], &str[1], 1, str, expected);