From b184672ec7f1433e5dc698cda7e61be8a6085aa6 Mon Sep 17 00:00:00 2001 From: Uzair Nawaz Date: Fri, 13 Jun 2025 16:48:24 +0000 Subject: [PATCH] [libc] Implemented wmemmove (#142245) Implemented wmemmove and added tests --- libc/config/linux/x86_64/entrypoints.txt | 1 + libc/include/wchar.yaml | 8 ++ libc/src/wchar/CMakeLists.txt | 12 +++ libc/src/wchar/wmemmove.cpp | 27 ++++++ libc/src/wchar/wmemmove.h | 22 +++++ libc/test/src/wchar/CMakeLists.txt | 10 ++ libc/test/src/wchar/wmemmove_test.cpp | 111 +++++++++++++++++++++++ 7 files changed, 191 insertions(+) create mode 100644 libc/src/wchar/wmemmove.cpp create mode 100644 libc/src/wchar/wmemmove.h create mode 100644 libc/test/src/wchar/wmemmove_test.cpp diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index c993ef8303a5..aa2079faed40 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -367,6 +367,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.wchar.btowc libc.src.wchar.wcslen libc.src.wchar.wctob + libc.src.wchar.wmemmove libc.src.wchar.wmemset libc.src.wchar.wcschr libc.src.wchar.wcsncmp diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 57f4f6660827..1af15a6c112b 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -112,6 +112,14 @@ functions: - type: __restrict wchar_t * - type: const __restrict wchar_t * - type: size_t + - name: wmemmove + standards: + - stdc + return_type: wchar_t * + arguments: + - type: wchar_t * + - type: const wchar_t * + - type: size_t - name: wcsncpy standards: - stdc diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 4b8802ede5f5..491dd5b34340 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -131,6 +131,18 @@ add_entrypoint_object( libc.hdr.wchar_macros ) +add_entrypoint_object( + wmemmove + SRCS + wmemmove.cpp + HDRS + wmemmove.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.wchar_macros + libc.src.__support.macros.null_check +) + add_entrypoint_object( wcsncpy SRCS diff --git a/libc/src/wchar/wmemmove.cpp b/libc/src/wchar/wmemmove.cpp new file mode 100644 index 000000000000..3282077003bd --- /dev/null +++ b/libc/src/wchar/wmemmove.cpp @@ -0,0 +1,27 @@ +//===-- Implementation of wmemmove ----------------------------------------===// +// +// 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/wchar/wmemmove.h" + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/null_check.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(wchar_t *, wmemmove, + (wchar_t * dest, const wchar_t *src, size_t n)) { + LIBC_CRASH_ON_NULLPTR(dest); + LIBC_CRASH_ON_NULLPTR(src); + + __builtin_memmove(dest, src, n * sizeof(wchar_t)); + return dest; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/wmemmove.h b/libc/src/wchar/wmemmove.h new file mode 100644 index 000000000000..b4c31ac7b397 --- /dev/null +++ b/libc/src/wchar/wmemmove.h @@ -0,0 +1,22 @@ +//===-- Implementation header for wmemmove --------------------------------===// +// +// 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_WCHAR_WMEMMOVE_H +#define LLVM_LIBC_SRC_WCHAR_WMEMMOVE_H + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t n); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_WMEMMOVE_H diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index 6293e8e3d55c..4990b6953348 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -145,6 +145,16 @@ add_libc_test( libc.src.wchar.wmemcpy ) +add_libc_test( + wmemmove_test + SUITE + libc_wchar_unittests + SRCS + wmemmove_test.cpp + DEPENDS + libc.src.wchar.wmemmove +) + add_libc_test( wcsncpy_test SUITE diff --git a/libc/test/src/wchar/wmemmove_test.cpp b/libc/test/src/wchar/wmemmove_test.cpp new file mode 100644 index 000000000000..d23aa0f0b3af --- /dev/null +++ b/libc/test/src/wchar/wmemmove_test.cpp @@ -0,0 +1,111 @@ +//===-- Unittests for wmemmove --------------------------------------------===// +// +// 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 "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/wchar/wmemmove.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcWMemmoveTest, MoveZeroByte) { + wchar_t buffer[] = {L'a', L'b', L'y', L'z'}; + + wchar_t *ret = LIBC_NAMESPACE::wmemmove(buffer, buffer + 2, 0); + EXPECT_EQ(ret, buffer); + + const wchar_t expected[] = {L'a', L'b', L'y', L'z'}; + EXPECT_TRUE(buffer[0] == expected[0]); + EXPECT_TRUE(buffer[1] == expected[1]); + EXPECT_TRUE(buffer[2] == expected[2]); + EXPECT_TRUE(buffer[3] == expected[3]); +} + +TEST(LlvmLibcWMemmoveTest, DstAndSrcPointToSameAddress) { + wchar_t buffer[] = {L'a', L'b'}; + + wchar_t *ret = LIBC_NAMESPACE::wmemmove(buffer, buffer, 1); + EXPECT_EQ(ret, buffer); + + const wchar_t expected[] = {L'a', L'b'}; + EXPECT_TRUE(buffer[0] == expected[0]); + EXPECT_TRUE(buffer[1] == expected[1]); +} + +TEST(LlvmLibcWMemmoveTest, DstStartsBeforeSrc) { + // Set boundary at beginning and end for not overstepping when + // copy forward or backward. + wchar_t buffer[] = {L'z', L'a', L'b', L'c', L'z'}; + + wchar_t *dst = buffer + 1; + wchar_t *ret = LIBC_NAMESPACE::wmemmove(dst, buffer + 2, 2); + EXPECT_EQ(ret, dst); + + const wchar_t expected[] = {L'z', L'b', L'c', L'c', L'z'}; + EXPECT_TRUE(buffer[0] == expected[0]); + EXPECT_TRUE(buffer[1] == expected[1]); + EXPECT_TRUE(buffer[2] == expected[2]); + EXPECT_TRUE(buffer[3] == expected[3]); + EXPECT_TRUE(buffer[4] == expected[4]); +} + +TEST(LlvmLibcWMemmoveTest, DstStartsAfterSrc) { + wchar_t buffer[] = {L'z', L'a', L'b', L'c', L'z'}; + + wchar_t *dst = buffer + 2; + wchar_t *ret = LIBC_NAMESPACE::wmemmove(dst, buffer + 1, 2); + EXPECT_EQ(ret, dst); + + const wchar_t expected[] = {L'z', L'a', L'a', L'b', L'z'}; + EXPECT_TRUE(buffer[0] == expected[0]); + EXPECT_TRUE(buffer[1] == expected[1]); + EXPECT_TRUE(buffer[2] == expected[2]); + EXPECT_TRUE(buffer[3] == expected[3]); + EXPECT_TRUE(buffer[4] == expected[4]); +} + +// e.g. `Dst` follow `src`. +// str: [abcdefghij] +// [__src_____] +// [_____Dst__] +TEST(LlvmLibcWMemmoveTest, SrcFollowDst) { + wchar_t buffer[] = {L'z', L'a', L'b', L'z'}; + + wchar_t *dst = buffer + 1; + wchar_t *ret = LIBC_NAMESPACE::wmemmove(dst, buffer + 2, 1); + EXPECT_EQ(ret, dst); + + const char expected[] = {L'z', L'b', L'b', L'z'}; + EXPECT_TRUE(buffer[0] == expected[0]); + EXPECT_TRUE(buffer[1] == expected[1]); + EXPECT_TRUE(buffer[2] == expected[2]); + EXPECT_TRUE(buffer[3] == expected[3]); +} + +TEST(LlvmLibcWMemmoveTest, DstFollowSrc) { + wchar_t buffer[] = {L'z', L'a', L'b', L'z'}; + + wchar_t *dst = buffer + 2; + wchar_t *ret = LIBC_NAMESPACE::wmemmove(dst, buffer + 1, 1); + EXPECT_EQ(ret, dst); + + const char expected[] = {L'z', L'a', L'a', L'z'}; + EXPECT_TRUE(buffer[0] == expected[0]); + EXPECT_TRUE(buffer[1] == expected[1]); + EXPECT_TRUE(buffer[2] == expected[2]); + EXPECT_TRUE(buffer[3] == expected[3]); +} + +#if defined(LIBC_ADD_NULL_CHECKS) && !defined(LIBC_HAS_SANITIZER) +TEST(LlvmLibcWMemmoveTest, NullptrCrash) { + wchar_t buffer[] = {L'a', L'b'}; + // Passing in a nullptr should crash the program. + EXPECT_DEATH([&buffer] { LIBC_NAMESPACE::wmemmove(buffer, nullptr, 2); }, + WITH_SIGNAL(-1)); + EXPECT_DEATH([&buffer] { LIBC_NAMESPACE::wmemmove(nullptr, buffer, 2); }, + WITH_SIGNAL(-1)); +} +#endif // LIBC_HAS_ADDRESS_SANITIZER