Skip to content

Commit 5a9630b

Browse files
committed
[libc] Adds implementation for memrchr.
Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D84469
1 parent 4a577c3 commit 5a9630b

File tree

9 files changed

+197
-2
lines changed

9 files changed

+197
-2
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set(TARGET_LIBC_ENTRYPOINTS
1313
libc.src.string.strchr
1414
libc.src.string.strstr
1515
libc.src.string.strnlen
16+
libc.src.string.memrchr
1617
)
1718

1819
set(TARGET_LIBM_ENTRYPOINTS

libc/config/linux/api.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ def StringAPI : PublicAPI<"string.h"> {
216216
"strtok",
217217
"strerror",
218218
"strlen",
219-
"strnlen"
219+
"strnlen",
220+
"memrchr"
220221
];
221222

222223
let TypeDeclarations = [

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ set(TARGET_LIBC_ENTRYPOINTS
3131
libc.src.string.strchr
3232
libc.src.string.strstr
3333
libc.src.string.strnlen
34+
libc.src.string.memrchr
3435

3536
# sys/mman.h entrypoints
3637
libc.src.sys.mman.mmap

libc/spec/gnu_ext.td

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,22 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> {
1212
>,
1313
]
1414
>;
15+
16+
HeaderSpec String = HeaderSpec<
17+
"string.h",
18+
[], // Macros
19+
[], // Types
20+
[], // Enumerations
21+
[
22+
FunctionSpec<
23+
"memrchr",
24+
RetValSpec<VoidPtr>,
25+
[ArgSpec<VoidPtr>, ArgSpec<IntType>, ArgSpec<SizeTType>]
26+
>,
27+
]
28+
>;
1529

1630
let Headers = [
17-
Math,
31+
Math, String,
1832
];
1933
}

libc/src/string/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ add_entrypoint_object(
7878
.memchr
7979
)
8080

81+
add_entrypoint_object(
82+
memrchr
83+
SRCS
84+
memrchr.cpp
85+
HDRS
86+
memrchr.h
87+
)
88+
8189
# Helper to define a function with multiple implementations
8290
# - Computes flags to satisfy required/rejected features and arch,
8391
# - Declares an entry point,

libc/src/string/memrchr.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===-- Implementation of memrchr -----------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/string/memrchr.h"
10+
#include "src/__support/common.h"
11+
#include <stddef.h>
12+
13+
namespace __llvm_libc {
14+
15+
void *LLVM_LIBC_ENTRYPOINT(memrchr)(const void *src, int c, size_t n) {
16+
const unsigned char *str = reinterpret_cast<const unsigned char *>(src);
17+
const unsigned char ch = c;
18+
for (; n != 0; --n) {
19+
const unsigned char *s = str + n - 1;
20+
if (*s == ch)
21+
return const_cast<unsigned char *>(s);
22+
}
23+
return nullptr;
24+
}
25+
26+
} // namespace __llvm_libc

libc/src/string/memrchr.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for memrchr -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STRING_MEMRCHR_H
10+
#define LLVM_LIBC_SRC_STRING_MEMRCHR_H
11+
12+
#include <stddef.h>
13+
14+
namespace __llvm_libc {
15+
16+
void *memrchr(const void *src, int c, size_t n);
17+
18+
} // namespace __llvm_libc
19+
20+
#endif // LLVM_LIBC_SRC_STRING_MEMRCHR_H

libc/test/src/string/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ add_libc_unittest(
8282
libc.src.string.strnlen
8383
)
8484

85+
add_libc_unittest(
86+
memrchr_test
87+
SUITE
88+
libc_string_unittests
89+
SRCS
90+
memrchr_test.cpp
91+
DEPENDS
92+
libc.src.string.memrchr
93+
)
94+
8595
# Tests all implementations that can run on the host.
8696
function(add_libc_multi_impl_test name)
8797
get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations)

libc/test/src/string/memrchr_test.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===-- Unittests for memrchr ---------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/string/memrchr.h"
10+
#include "utils/UnitTest/Test.h"
11+
#include <stddef.h>
12+
13+
// A helper function that calls memrchr and abstracts away the explicit cast for
14+
// readability purposes.
15+
const char *call_memrchr(const void *src, int c, size_t size) {
16+
return reinterpret_cast<const char *>(__llvm_libc::memrchr(src, c, size));
17+
}
18+
19+
TEST(MemRChrTest, FindsCharacterAfterNullTerminator) {
20+
// memrchr should continue searching after a null terminator.
21+
const size_t size = 6;
22+
const unsigned char src[size] = {'a', '\0', 'b', 'c', 'd', '\0'};
23+
// Should return 'b', 'c', 'd', '\0' even when after null terminator.
24+
ASSERT_STREQ(call_memrchr(src, 'b', size), "bcd");
25+
}
26+
27+
TEST(MemRChrTest, FindsCharacterInNonNullTerminatedCollection) {
28+
const size_t size = 3;
29+
const unsigned char src[size] = {'a', 'b', 'c'};
30+
// Should return 'b', 'c'.
31+
const char *ret = call_memrchr(src, 'b', size);
32+
ASSERT_EQ(ret[0], 'b');
33+
ASSERT_EQ(ret[1], 'c');
34+
}
35+
36+
TEST(MemRChrTest, FindsFirstCharacter) {
37+
const size_t size = 6;
38+
const unsigned char src[size] = {'a', 'b', 'c', 'd', 'e', '\0'};
39+
// Should return original array since 'a' is the first character.
40+
ASSERT_STREQ(call_memrchr(src, 'a', size), "abcde");
41+
}
42+
43+
TEST(MemRChrTest, FindsMiddleCharacter) {
44+
const size_t size = 6;
45+
const unsigned char src[size] = {'a', 'b', 'c', 'd', 'e', '\0'};
46+
// Should return characters after (and including) 'c'.
47+
ASSERT_STREQ(call_memrchr(src, 'c', size), "cde");
48+
}
49+
50+
TEST(MemRChrTest, FindsLastCharacterThatIsNotNullTerminator) {
51+
const size_t size = 6;
52+
const unsigned char src[size] = {'a', 'b', 'c', 'd', 'e', '\0'};
53+
// Should return 'e' and null-terminator.
54+
ASSERT_STREQ(call_memrchr(src, 'e', size), "e");
55+
}
56+
57+
TEST(MemRChrTest, FindsNullTerminator) {
58+
const size_t size = 6;
59+
const unsigned char src[size] = {'a', 'b', 'c', 'd', 'e', '\0'};
60+
// Should return null terminator.
61+
ASSERT_STREQ(call_memrchr(src, '\0', size), "");
62+
}
63+
64+
TEST(MemRChrTest, CharacterNotWithinStringShouldReturnNullptr) {
65+
const size_t size = 4;
66+
const unsigned char src[size] = {'1', '2', '3', '?'};
67+
// Since 'z' is not within 'characters', should return nullptr.
68+
ASSERT_STREQ(call_memrchr(src, 'z', size), nullptr);
69+
}
70+
71+
TEST(MemRChrTest, CharacterNotWithinSizeShouldReturnNullptr) {
72+
const unsigned char src[5] = {'1', '2', '3', '4', '\0'};
73+
// Since '4' is not within the first 2 characters, this should return nullptr.
74+
const size_t size = 2;
75+
ASSERT_STREQ(call_memrchr(src, '4', size), nullptr);
76+
}
77+
78+
TEST(MemRChrTest, ShouldFindLastOfDuplicates) {
79+
size_t size = 12; // 11 characters + null terminator.
80+
const char *dups = "abc1def1ghi";
81+
// 1 is duplicated in 'dups', but it should find the last copy.
82+
ASSERT_STREQ(call_memrchr(dups, '1', size), "1ghi");
83+
84+
const char *repeated = "XXXXX";
85+
size = 6; // 5 characters + null terminator.
86+
// Should return the last X with the null terminator.
87+
ASSERT_STREQ(call_memrchr(repeated, 'X', size), "X");
88+
}
89+
90+
TEST(MemRChrTest, EmptyStringShouldOnlyMatchNullTerminator) {
91+
const size_t size = 1; // Null terminator.
92+
const char *empty_string = "";
93+
// Null terminator should match.
94+
ASSERT_STREQ(call_memrchr(empty_string, '\0', size), "");
95+
// All other characters should not match.
96+
ASSERT_STREQ(call_memrchr(empty_string, 'A', size), nullptr);
97+
ASSERT_STREQ(call_memrchr(empty_string, '9', size), nullptr);
98+
ASSERT_STREQ(call_memrchr(empty_string, '?', size), nullptr);
99+
}
100+
101+
TEST(MemRChrTest, SignedCharacterFound) {
102+
char c = -1;
103+
const size_t size = 1;
104+
char src[size] = {c};
105+
const char *actual = call_memrchr(src, c, size);
106+
// Should find the last character 'c'.
107+
ASSERT_EQ(actual[0], c);
108+
}
109+
110+
TEST(MemRChrTest, ZeroLengthShouldReturnNullptr) {
111+
const unsigned char src[4] = {'a', 'b', 'c', '\0'};
112+
// This will iterate over exactly zero characters, so should return nullptr.
113+
ASSERT_STREQ(call_memrchr(src, 'd', 0), nullptr);
114+
}

0 commit comments

Comments
 (0)