diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -5,6 +5,7 @@ # string.h entrypoints libc.src.string.bzero libc.src.string.memchr + libc.src.string.memcmp libc.src.string.memcpy libc.src.string.memset libc.src.string.memrchr 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 @@ -22,6 +22,7 @@ # string.h entrypoints libc.src.string.bzero libc.src.string.memchr + libc.src.string.memcmp libc.src.string.memcpy libc.src.string.memrchr libc.src.string.memset 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 @@ -86,6 +86,14 @@ memrchr.h ) +add_entrypoint_object( + memcmp + SRCS + memcmp.cpp + HDRS + memcmp.h +) + # Helper to define a function with multiple implementations # - Computes flags to satisfy required/rejected features and arch, # - Declares an entry point, diff --git a/libc/src/string/memcmp.h b/libc/src/string/memcmp.h new file mode 100644 --- /dev/null +++ b/libc/src/string/memcmp.h @@ -0,0 +1,20 @@ +//===-- Implementation header for memcmp ------------------------*- 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_MEMCMP_H +#define LLVM_LIBC_SRC_STRING_MEMCMP_H + +#include + +namespace __llvm_libc { + +int memcmp(const void *left, const void *right, size_t n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_MEMCMP_H diff --git a/libc/src/string/memcmp.cpp b/libc/src/string/memcmp.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/memcmp.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of memcmp ------------------------------------------===// +// +// 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/memcmp.h" + +#include "src/__support/common.h" +#include + +namespace __llvm_libc { + +// TODO: Look at benefits for comparing words at a time. +int LLVM_LIBC_ENTRYPOINT(memcmp)(const void *left, const void *right, + size_t n) { + const unsigned char *l = reinterpret_cast(left); + const unsigned char *r = reinterpret_cast(right); + for (; n && *l == *r; --n, ++l, ++r) + ; + return n ? *l - *r : 0; +} + +} // 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 @@ -92,6 +92,16 @@ libc.src.string.memrchr ) +add_libc_unittest( + memcmp_test + SUITE + libc_string_unittests + SRCS + memcmp_test.cpp + DEPENDS + libc.src.string.memcmp +) + # Tests all implementations that can run on the host. function(add_libc_multi_impl_test name) get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations) diff --git a/libc/test/src/string/memcmp_test.cpp b/libc/test/src/string/memcmp_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/memcmp_test.cpp @@ -0,0 +1,117 @@ +//===-- Unittests for memcmp ----------------------------------------------===// +// +// 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/memcmp.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(MemCmpTest, SizeZeroShouldReturnZero) { + const char *s1 = "abc"; + const char *s2 = "def"; + ASSERT_EQ(__llvm_libc::memcmp(s1, s2, 0), 0); +} + +TEST(MemCmpTest, ShouldOnlyCompareUpToSizeN) { + const char *s1 = "12XC"; + const char *s2 = "12Yc"; + int result = __llvm_libc::memcmp(s1, s2, 2); + ASSERT_EQ(result, 0); + result = __llvm_libc::memcmp(s1, s2, 3); + ASSERT_EQ(result, 'X' - 'Y'); + // Increasing size still finds the FIRST difference. + result = __llvm_libc::memcmp(s1, s2, 4); + ASSERT_EQ(result, 'X' - 'Y'); +} + +TEST(MemCmpTest, EmptyStringsShouldReturnZero) { + const char *s1 = ""; + const char *s2 = ""; + int result = __llvm_libc::memcmp(s1, s2, 1); + ASSERT_EQ(result, 0); + + // Verify operands reversed. + result = __llvm_libc::memcmp(s2, s1, 1); + ASSERT_EQ(result, 0); +} + +TEST(MemCmpTest, EmptyStringShouldNotEqualNonEmptyString) { + const char *empty = ""; + const char *s2 = "abc"; + int result = __llvm_libc::memcmp(empty, s2, 3); + // This should be '\0' - 'a' = -97 + ASSERT_EQ(result, -97); + + // Similar case if empty string is second argument. + const char *s3 = "123"; + result = __llvm_libc::memcmp(s3, empty, 3); + // This should be '1' - '\0' = 49 + ASSERT_EQ(result, 49); +} + +TEST(MemCmpTest, EqualStringsShouldReturnZero) { + const char *s1 = "abc"; + const char *s2 = "abc"; + int result = __llvm_libc::memcmp(s1, s2, 3); + ASSERT_EQ(result, 0); + + // Verify operands reversed. + result = __llvm_libc::memcmp(s2, s1, 3); + ASSERT_EQ(result, 0); +} + +TEST(MemCmpTest, ShouldReturnResultOfFirstDifference) { + const char *s1 = "_B42"; + const char *s2 = "_C55"; + int result = __llvm_libc::memcmp(s1, s2, 4); + // This should return 'B' - 'C' = -1. + ASSERT_EQ(result, -1); + + // Verify operands reversed. + result = __llvm_libc::memcmp(s2, s1, 4); + // This should return 'C' - 'B' = 1. + ASSERT_EQ(result, 1); +} + +TEST(MemCmpTest, CapitalizedLetterShouldNotBeEqual) { + const char *s1 = "abcd"; + const char *s2 = "abCd"; + int result = __llvm_libc::memcmp(s1, s2, 4); + // 'c' - 'C' = 32. + ASSERT_EQ(result, 32); + + // Verify operands reversed. + result = __llvm_libc::memcmp(s2, s1, 4); + // 'C' - 'c' = -32. + ASSERT_EQ(result, -32); +} + +TEST(MemCmpTest, UnequalLengthStringsShouldNotReturnZero) { + const char *s1 = "abc"; + const char *s2 = "abcd"; + int result = __llvm_libc::memcmp(s1, s2, 4); + // '\0' - 'd' = -100. + ASSERT_EQ(result, -100); + + // Verify operands reversed. + result = __llvm_libc::memcmp(s2, s1, 4); + // 'd' - '\0' = 100. + ASSERT_EQ(result, 100); +} + +TEST(MemCmpTest, StringArgumentSwapChangesSign) { + const char *a = "a"; + const char *b = "b"; + int result = __llvm_libc::memcmp(b, a, 1); + // 'b' - 'a' = 1. + ASSERT_EQ(result, 1); + + result = __llvm_libc::memcmp(a, b, 1); + // 'a' - 'b' = -1. + ASSERT_EQ(result, -1); +}