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 @@ -10,6 +10,7 @@ libc.src.string.strcat libc.src.string.strlen libc.src.string.memchr + libc.src.string.strchr ) set(TARGET_LIBM_ENTRYPOINTS 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 @@ -28,6 +28,7 @@ libc.src.string.strlen libc.src.string.strcmp libc.src.string.memchr + libc.src.string.strchr # sys/mman.h entrypoints libc.src.sys.mman.mmap 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 @@ -52,6 +52,15 @@ memchr.h ) +add_entrypoint_object( + strchr + SRCS + strchr.cpp + HDRS + strchr.h + DEPENDS + .strlen +) # Helper to define a function with multiple implementations # - Computes flags to satisfy required/rejected features and arch, diff --git a/libc/src/string/strchr.h b/libc/src/string/strchr.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strchr.h @@ -0,0 +1,18 @@ +//===-- Implementation header for strchr ------------------------*- 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_STRCHR_H +#define LLVM_LIBC_SRC_STRING_STRCHR_H + +namespace __llvm_libc { + +char *strchr(const char *src, int c); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCHR_H diff --git a/libc/src/string/strchr.cpp b/libc/src/string/strchr.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strchr.cpp @@ -0,0 +1,27 @@ +//===-- Implementation of strchr ------------------------------------------===// +// +// 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/strchr.h" +#include "src/string/strlen.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +// TODO: Look at performance benefits of comparing words. +char *LLVM_LIBC_ENTRYPOINT(strchr)(const char *src, int c) { + char *str = const_cast(src); + if (!c) + return str + __llvm_libc::strlen(str); + + for (; *str && *str != c; ++str) + ; + return *str == c ? str : nullptr; +} + +} // 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 @@ -52,6 +52,16 @@ libc.src.string.memchr ) +add_libc_unittest( + strchr_test + SUITE + libc_string_unittests + SRCS + strchr_test.cpp + DEPENDS + libc.src.string.strchr +) + # 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/strchr_test.cpp b/libc/test/src/string/strchr_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strchr_test.cpp @@ -0,0 +1,66 @@ +//===-- Unittests for strchr ----------------------------------------------===// +// +// 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/strchr.h" +#include "utils/UnitTest/Test.h" + +TEST(StrChrTest, FindsFirstCharacter) { + // Should return original string since 'a' is the first character. + ASSERT_STREQ(__llvm_libc::strchr("abcde", 'a'), "abcde"); +} + +TEST(StrChrTest, FindsMiddleCharacter) { + // Should return characters after (and including) 'c'. + ASSERT_STREQ(__llvm_libc::strchr("abcde", 'c'), "cde"); +} + +TEST(StrChrTest, FindsLastCharacterThatIsNotNullTerminator) { + // Should return 'e' and null-terminator. + ASSERT_STREQ(__llvm_libc::strchr("abcde", 'e'), "e"); +} + +TEST(StrChrTest, FindsNullTerminator) { + // Should return null terminator. + ASSERT_STREQ(__llvm_libc::strchr("abcde", '\0'), ""); +} + +TEST(StrChrTest, CharacterNotWithinStringShouldReturnNullptr) { + // Since 'z' is not within the string, should return nullptr. + ASSERT_STREQ(__llvm_libc::strchr("123?", 'z'), nullptr); +} + +TEST(StrChrTest, TheSourceShouldNotChange) { + const char *src = "abcde"; + const char *src_copy = src; + // When the character is found, the source string should not change. + __llvm_libc::strchr(src, 'd'); + ASSERT_STREQ(src, src_copy); + // Same case for when the character is not found. + __llvm_libc::strchr(src, 'z'); + ASSERT_STREQ(src, src_copy); +} + +TEST(StrChrTest, ShouldFindFirstOfDuplicates) { + // '1' is duplicated in the string, but it should find the first copy. + ASSERT_STREQ(__llvm_libc::strchr("abc1def1ghi", '1'), "1def1ghi"); +} + +TEST(StrChrTest, EmptyStringShouldOnlyMatchNullTerminator) { + // Null terminator should match. + ASSERT_STREQ(__llvm_libc::strchr("", '\0'), ""); + // All other characters should not match. + ASSERT_STREQ(__llvm_libc::strchr("", 'Z'), nullptr); + ASSERT_STREQ(__llvm_libc::strchr("", '3'), nullptr); + ASSERT_STREQ(__llvm_libc::strchr("", '*'), nullptr); +} + +TEST(StrChrTest, SingleRepeatedCharacterShouldReturnFirstInstance) { + const char *dups = "XXXXX"; + // Should return original string since 'X' is the first character. + ASSERT_STREQ(__llvm_libc::strchr(dups, 'X'), dups); +}