From bc2b1614081c23b3c42917538fa54d67917c998f Mon Sep 17 00:00:00 2001 From: Caslyn Tonelli Date: Mon, 3 Apr 2023 11:04:48 -0700 Subject: [PATCH] [libc] Add strchrnul implementation Introduce strchrnul implementation and unit tests. Submitting on behalf of Caslyn@ Differential Revision: https://reviews.llvm.org/D147346 --- libc/config/baremetal/arm/entrypoints.txt | 1 + libc/config/darwin/arm/entrypoints.txt | 1 + libc/config/darwin/x86_64/entrypoints.txt | 1 + libc/config/gpu/entrypoints.txt | 1 + libc/config/linux/aarch64/entrypoints.txt | 1 + libc/config/linux/arm/entrypoints.txt | 1 + libc/config/linux/riscv64/entrypoints.txt | 1 + libc/config/linux/x86_64/entrypoints.txt | 1 + libc/config/windows/entrypoints.txt | 1 + libc/spec/gnu_ext.td | 5 ++ libc/src/string/CMakeLists.txt | 10 ++++ libc/src/string/strchrnul.cpp | 23 ++++++++ libc/src/string/strchrnul.h | 18 ++++++ libc/test/src/string/CMakeLists.txt | 10 ++++ libc/test/src/string/strchr_test.cpp | 2 +- libc/test/src/string/strchrnul_test.cpp | 96 +++++++++++++++++++++++++++++++ 16 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 libc/src/string/strchrnul.cpp create mode 100644 libc/src/string/strchrnul.h create mode 100644 libc/test/src/string/strchrnul_test.cpp diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 8f173d5..dbc6b00 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -38,6 +38,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index f672902..62996c1 100644 --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -38,6 +38,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt index 42848c8..122e7fc 100644 --- a/libc/config/darwin/x86_64/entrypoints.txt +++ b/libc/config/darwin/x86_64/entrypoints.txt @@ -32,6 +32,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpncpy libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt index ee39c64..4841ed1 100644 --- a/libc/config/gpu/entrypoints.txt +++ b/libc/config/gpu/entrypoints.txt @@ -34,6 +34,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 7656be9..92b6f27 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -47,6 +47,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 7bafe54..5e17133 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -38,6 +38,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt index b502df6..3f4fbed 100644 --- a/libc/config/linux/riscv64/entrypoints.txt +++ b/libc/config/linux/riscv64/entrypoints.txt @@ -47,6 +47,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcoll libc.src.string.strcpy diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 9936bf7..238ec84 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -47,6 +47,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcoll libc.src.string.strcpy diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index d7b15cf..8f5c4b6 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -35,6 +35,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr + libc.src.string.strchrnul libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td index f3311e3..52fc061 100644 --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -73,6 +73,11 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> { RetValSpec, [ArgSpec, ArgSpec] >, + FunctionSpec< + "strchrnul", + RetValSpec, + [ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index 6dfb6d7..034df80 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -108,6 +108,16 @@ add_entrypoint_object( ) add_entrypoint_object( + strchrnul + SRCS + strchrnul.cpp + HDRS + strchrnul.h + DEPENDS + .string_utils +) + +add_entrypoint_object( strcmp SRCS strcmp.cpp diff --git a/libc/src/string/strchrnul.cpp b/libc/src/string/strchrnul.cpp new file mode 100644 index 0000000..4dc28cd --- /dev/null +++ b/libc/src/string/strchrnul.cpp @@ -0,0 +1,23 @@ +//===-- Implementation of strchrnul --------------------------------------===// +// +// 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/strchrnul.h" +#include "src/string/string_utils.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, strchrnul, (const char *src, int c)) { + const char ch = static_cast(c); + for (; *src && *src != ch; ++src) + ; + return const_cast(src); +} + +} // namespace __llvm_libc diff --git a/libc/src/string/strchrnul.h b/libc/src/string/strchrnul.h new file mode 100644 index 0000000..2ba07ac --- /dev/null +++ b/libc/src/string/strchrnul.h @@ -0,0 +1,18 @@ +//===-- Implementation header for strchrnul --------------------*- 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_STRCHRNUL_H +#define LLVM_LIBC_SRC_STRING_STRCHRNUL_H + +namespace __llvm_libc { + +char *strchrnul(const char *src, int c); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCHRNUL_H diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index 67f49ab..e5c4880 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -95,6 +95,16 @@ add_libc_unittest( ) add_libc_unittest( + strchrnul_test + SUITE + libc_string_unittests + SRCS + strchrnul_test.cpp + DEPENDS + libc.src.string.strchrnul +) + +add_libc_unittest( strcmp_test SUITE libc_string_unittests diff --git a/libc/test/src/string/strchr_test.cpp b/libc/test/src/string/strchr_test.cpp index 0dedd28..c619279 100644 --- a/libc/test/src/string/strchr_test.cpp +++ b/libc/test/src/string/strchr_test.cpp @@ -58,7 +58,7 @@ TEST(LlvmLibcStrChrTest, TheSourceShouldNotChange) { // Same case for when the character is not found. __llvm_libc::strchr(src, 'z'); ASSERT_STREQ(src, "abcde"); - // Same case for when looking for nullptr. + // Same case for when looking for null terminator. __llvm_libc::strchr(src, '\0'); ASSERT_STREQ(src, "abcde"); } diff --git a/libc/test/src/string/strchrnul_test.cpp b/libc/test/src/string/strchrnul_test.cpp new file mode 100644 index 0000000..e4ca729 --- /dev/null +++ b/libc/test/src/string/strchrnul_test.cpp @@ -0,0 +1,96 @@ +//===-- Unittests for strchrnul -------------------------------------------===// +// +// 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/strchrnul.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcStrChrNulTest, FindsFirstCharacter) { + const char *src = "abcde"; + + // Should return original string since 'a' is the first character. + ASSERT_STREQ(__llvm_libc::strchrnul(src, 'a'), "abcde"); + // Source string should not change. + ASSERT_STREQ(src, "abcde"); +} + +TEST(LlvmLibcStrChrNulTest, FindsMiddleCharacter) { + const char *src = "abcde"; + + // Should return characters after (and including) 'c'. + ASSERT_STREQ(__llvm_libc::strchrnul(src, 'c'), "cde"); + // Source string should not change. + ASSERT_STREQ(src, "abcde"); +} + +TEST(LlvmLibcStrChrNulTest, FindsLastCharacterThatIsNotNullTerminator) { + const char *src = "abcde"; + + // Should return 'e' and null-terminator. + ASSERT_STREQ(__llvm_libc::strchrnul(src, 'e'), "e"); + // Source string should not change. + ASSERT_STREQ(src, "abcde"); +} + +TEST(LlvmLibcStrChrNulTest, FindsNullTerminator) { + const char *src = "abcde"; + + // Should return null terminator. + ASSERT_STREQ(__llvm_libc::strchrnul(src, '\0'), ""); + // Source string should not change. + ASSERT_STREQ(src, "abcde"); +} + +TEST(LlvmLibcStrChrNulTest, + CharacterNotWithinStringShouldReturnNullTerminator) { + const char *src = "123?"; + + // Since 'z' is not within the string, should return a pointer to the source + // string's null terminator. + char *result = __llvm_libc::strchrnul(src, 'z'); + ASSERT_EQ(*result, '\0'); + + char *term = const_cast(src) + 4; + ASSERT_EQ(result, term); +} + +TEST(LlvmLibcStrChrNulTest, TheSourceShouldNotChange) { + const char *src = "abcde"; + // When the character is found, the source string should not change. + __llvm_libc::strchrnul(src, 'd'); + ASSERT_STREQ(src, "abcde"); + // Same case for when the character is not found. + __llvm_libc::strchrnul(src, 'z'); + ASSERT_STREQ(src, "abcde"); + // Same case for when looking for null terminator. + __llvm_libc::strchrnul(src, '\0'); + ASSERT_STREQ(src, "abcde"); +} + +TEST(LlvmLibcStrChrNulTest, ShouldFindFirstOfDuplicates) { + // '1' is duplicated in the string, but it should find the first copy. + ASSERT_STREQ(__llvm_libc::strchrnul("abc1def1ghi", '1'), "1def1ghi"); + + const char *dups = "XXXXX"; + // Should return original string since 'X' is the first character. + ASSERT_STREQ(__llvm_libc::strchrnul(dups, 'X'), dups); +} + +TEST(LlvmLibcStrChrNulTest, EmptyStringShouldOnlyMatchNullTerminator) { + // Null terminator should match. + ASSERT_STREQ(__llvm_libc::strchrnul("", '\0'), ""); + + // All other characters should not match. + char *result = __llvm_libc::strchrnul("", 'Z'); + ASSERT_EQ(*result, '\0'); + + result = __llvm_libc::strchrnul("", '3'); + ASSERT_EQ(*result, '\0'); + + result = __llvm_libc::strchrnul("", '*'); + ASSERT_EQ(*result, '\0'); +} -- 2.7.4