Skip to content

Commit

Permalink
[libc] Add strchrnul implementation
Browse files Browse the repository at this point in the history
Introduce strchrnul implementation and unit tests.

Submitting on behalf of Caslyn@

Differential Revision: https://reviews.llvm.org/D147346
  • Loading branch information
Caslyn authored and zeroomega committed Apr 3, 2023
1 parent d9163b2 commit bc2b161
Show file tree
Hide file tree
Showing 16 changed files with 172 additions and 1 deletion.
1 change: 1 addition & 0 deletions libc/config/baremetal/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/darwin/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/darwin/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/gpu/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/riscv64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/windows/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions libc/spec/gnu_ext.td
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> {
RetValSpec<CharPtr>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strchrnul",
RetValSpec<CharPtr>,
[ArgSpec<ConstCharPtr>, ArgSpec<IntType>]
>,
]
>;

Expand Down
10 changes: 10 additions & 0 deletions libc/src/string/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ add_entrypoint_object(
strchr.h
)

add_entrypoint_object(
strchrnul
SRCS
strchrnul.cpp
HDRS
strchrnul.h
DEPENDS
.string_utils
)

add_entrypoint_object(
strcmp
SRCS
Expand Down
23 changes: 23 additions & 0 deletions libc/src/string/strchrnul.cpp
Original file line number Diff line number Diff line change
@@ -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<char>(c);
for (; *src && *src != ch; ++src)
;
return const_cast<char *>(src);
}

} // namespace __llvm_libc
18 changes: 18 additions & 0 deletions libc/src/string/strchrnul.h
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions libc/test/src/string/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ add_libc_unittest(
libc.src.string.strchr
)

add_libc_unittest(
strchrnul_test
SUITE
libc_string_unittests
SRCS
strchrnul_test.cpp
DEPENDS
libc.src.string.strchrnul
)

add_libc_unittest(
strcmp_test
SUITE
Expand Down
2 changes: 1 addition & 1 deletion libc/test/src/string/strchr_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
96 changes: 96 additions & 0 deletions libc/test/src/string/strchrnul_test.cpp
Original file line number Diff line number Diff line change
@@ -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<char *>(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');
}

0 comments on commit bc2b161

Please sign in to comment.