[libc-commits] [libc] [libc][search] implement POSIX `lsearch` function (PR #116870)
via libc-commits
libc-commits at lists.llvm.org
Tue Nov 19 12:28:53 PST 2024
https://github.com/duncpro updated https://github.com/llvm/llvm-project/pull/116870
>From 3414b94a10a5c212e12c7a8a147d7f8d0e71b034 Mon Sep 17 00:00:00 2001
From: Duncan <duncpro at icloud.com>
Date: Tue, 19 Nov 2024 15:10:22 -0500
Subject: [PATCH] [libc][search] implement POSIX `lsearch` function
---
libc/config/darwin/x86_64/entrypoints.txt | 3 +-
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/arm/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/docs/libc_search.rst | 2 +-
libc/newhdrgen/yaml/search.yaml | 10 +++
libc/spec/posix.td | 11 ++++
libc/src/search/CMakeLists.txt | 13 ++++
libc/src/search/lsearch.cpp | 43 ++++++++++++
libc/src/search/lsearch.h | 20 ++++++
libc/test/src/search/CMakeLists.txt | 10 +++
libc/test/src/search/lsearch_test.cpp | 79 +++++++++++++++++++++++
13 files changed, 193 insertions(+), 2 deletions(-)
create mode 100644 libc/src/search/lsearch.cpp
create mode 100644 libc/src/search/lsearch.h
create mode 100644 libc/test/src/search/lsearch_test.cpp
diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt
index 64eeed18f3819e..5efdf7ee086d7d 100644
--- a/libc/config/darwin/x86_64/entrypoints.txt
+++ b/libc/config/darwin/x86_64/entrypoints.txt
@@ -18,7 +18,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.ctype.toupper
# search.h entrypoints
- libc.src.search.lfind
+ libc.src.search.lfind
+ libc.src.search.lsearch
# string.h entrypoints
libc.src.string.bcmp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 74ca3742977a5f..a64fe17f4cacbd 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -961,6 +961,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.hsearch_r
libc.src.search.insque
libc.src.search.lfind
+ libc.src.search.lsearch
libc.src.search.remque
# threads.h entrypoints
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 31d81de06fb6b0..259fa1bd9d984d 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -185,6 +185,7 @@ if(LLVM_LIBC_FULL_BUILD)
list(APPEND TARGET_LIBC_ENTRYPOINTS
# search.h entrypoints
libc.src.search.lfind
+ libc.src.search.lsearch
# setjmp.h entrypoints
libc.src.setjmp.longjmp
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 5419462d4f5b3b..e1c7ecc89bc3ee 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -899,6 +899,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.hsearch_r
libc.src.search.insque
libc.src.search.lfind
+ libc.src.search.lsearch
libc.src.search.remque
# threads.h entrypoints
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 957e28bd66cc4c..7d3a4237be613c 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1044,6 +1044,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.hsearch_r
libc.src.search.insque
libc.src.search.lfind
+ libc.src.search.lsearch
libc.src.search.remque
# threads.h entrypoints
diff --git a/libc/docs/libc_search.rst b/libc/docs/libc_search.rst
index 774622d1e66c3f..7d31181427507f 100644
--- a/libc/docs/libc_search.rst
+++ b/libc/docs/libc_search.rst
@@ -43,7 +43,7 @@ hdestroy |check|
hsearch |check|
insque |check|
lfind |check|
-lsearch
+lsearch |check|
remque |check|
tdelete
tfind
diff --git a/libc/newhdrgen/yaml/search.yaml b/libc/newhdrgen/yaml/search.yaml
index a0c73bc679d819..38b950fc8a2314 100644
--- a/libc/newhdrgen/yaml/search.yaml
+++ b/libc/newhdrgen/yaml/search.yaml
@@ -68,3 +68,13 @@ functions:
- type: size_t *
- type: size_t
- type: __lsearchcompare_t
+ - name: lsearch
+ standards:
+ - POSIX
+ return_type: void *
+ arguments:
+ - type: const void *
+ - type: void *
+ - type: size_t *
+ - type: size_t
+ - type: __lsearchcompare_t
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index e354deef340f1b..5ba334888bd5f4 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -1632,6 +1632,17 @@ def POSIX : StandardSpec<"POSIX"> {
ArgSpec<SizeTType>,
ArgSpec<LSearchCompareT>
]
+ >,
+ FunctionSpec<
+ "lsearch",
+ RetValSpec<VoidPtr>,
+ [
+ ArgSpec<ConstVoidPtr>,
+ ArgSpec<VoidPtr>,
+ ArgSpec<SizeTPtr>,
+ ArgSpec<SizeTType>,
+ ArgSpec<LSearchCompareT>
+ ]
>
]
>;
diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt
index 497657f40f2f02..d78ea062342a1d 100644
--- a/libc/src/search/CMakeLists.txt
+++ b/libc/src/search/CMakeLists.txt
@@ -110,3 +110,16 @@ add_entrypoint_object(
libc.src.__support.CPP.cstddef
libc.src.__support.memory_size
)
+
+add_entrypoint_object(
+ lsearch
+ SRCS
+ lsearch.cpp
+ HDRS
+ lsearch.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.CPP.cstddef
+ libc.src.__support.memory_size
+ libc.src.string.memory_utils.inline_memcpy
+)
diff --git a/libc/src/search/lsearch.cpp b/libc/src/search/lsearch.cpp
new file mode 100644
index 00000000000000..a9f0aa3e7530fc
--- /dev/null
+++ b/libc/src/search/lsearch.cpp
@@ -0,0 +1,43 @@
+//===-- Implementation of lsearch -------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/lsearch.h"
+#include "src/__support/CPP/cstddef.h" // cpp::byte
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/memory_size.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+
+namespace LIBC_NAMESPACE_DECL {
+LLVM_LIBC_FUNCTION(void *, lsearch,
+ (const void *key, void *base, size_t *nmemb, size_t size,
+ int (*compar)(const void *, const void *))) {
+ if (key == nullptr || base == nullptr || nmemb == nullptr ||
+ compar == nullptr)
+ return nullptr;
+
+ size_t byte_len = 0;
+ if (internal::mul_overflow(*nmemb, size, &byte_len))
+ return nullptr;
+
+ cpp::byte *next = reinterpret_cast<cpp::byte *>(const_cast<void *>(base));
+ const cpp::byte *end = next + byte_len;
+
+ for (; next < end; next += size)
+ if (compar(key, next) == 0)
+ break;
+
+ if (next == end) {
+ LIBC_NAMESPACE::inline_memcpy(next, key, size);
+ *nmemb += 1;
+ }
+
+ return next;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/search/lsearch.h b/libc/src/search/lsearch.h
new file mode 100644
index 00000000000000..c95e01d396cd06
--- /dev/null
+++ b/libc/src/search/lsearch.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for lsearch -----------------------*- 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_SEARCH_LSEARCH_H
+#define LLVM_LIBC_SRC_SEARCH_LSEARCH_H
+
+#include "src/__support/macros/config.h"
+#include <stddef.h> // size_t
+
+namespace LIBC_NAMESPACE_DECL {
+void *lsearch(const void *key, void *base, size_t *nmemb, size_t size,
+ int (*compar)(const void *, const void *));
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SEARCH_LSEARCH_H
diff --git a/libc/test/src/search/CMakeLists.txt b/libc/test/src/search/CMakeLists.txt
index a1f9aac2094c9b..9f34d4d3fd2597 100644
--- a/libc/test/src/search/CMakeLists.txt
+++ b/libc/test/src/search/CMakeLists.txt
@@ -35,3 +35,13 @@ add_libc_unittest(
DEPENDS
libc.src.search.lfind
)
+
+add_libc_unittest(
+ lsearch_test
+ SUITE
+ libc_search_unittests
+ SRCS
+ lsearch_test.cpp
+ DEPENDS
+ libc.src.search.lsearch
+)
diff --git a/libc/test/src/search/lsearch_test.cpp b/libc/test/src/search/lsearch_test.cpp
new file mode 100644
index 00000000000000..ab6ee3dcf8ac57
--- /dev/null
+++ b/libc/test/src/search/lsearch_test.cpp
@@ -0,0 +1,79 @@
+//===-- Unittests for lfind -----------------------------------------------===//
+//
+// 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/search/lsearch.h"
+#include "test/UnitTest/Test.h"
+
+int compar(const void *a, const void *b) {
+ return *reinterpret_cast<const int *>(a) != *reinterpret_cast<const int *>(b);
+}
+
+TEST(LlvmLibcLsearchTest, SearchHead) {
+ int list[4] = {1, 2, 3, 4};
+ size_t len = 3;
+ int key = 1;
+ void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+ ASSERT_EQ(static_cast<int *>(ret), &list[0]);
+ ASSERT_EQ(len, static_cast<size_t>(3));
+ ASSERT_EQ(list[1], 2);
+ ASSERT_EQ(list[2], 3);
+ ASSERT_EQ(list[3], 4);
+ ASSERT_EQ(list[3], 4);
+}
+
+TEST(LlvmLibcLsearchTest, SearchMiddle) {
+ int list[4] = {1, 2, 3, 4};
+ size_t len = 3;
+ int key = 2;
+ void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+ ASSERT_EQ(static_cast<int *>(ret), &list[1]);
+ ASSERT_EQ(len, static_cast<size_t>(3));
+ ASSERT_EQ(list[0], 1);
+ ASSERT_EQ(list[1], 2);
+ ASSERT_EQ(list[2], 3);
+ ASSERT_EQ(list[3], 4);
+}
+
+TEST(LlvmLibcLsearchTest, SearchTail) {
+ int list[4] = {1, 2, 3, 4};
+ size_t len = 3;
+ int key = 3;
+ void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+ ASSERT_EQ(static_cast<int *>(ret), &list[2]);
+ ASSERT_EQ(len, static_cast<size_t>(3));
+ ASSERT_EQ(list[0], 1);
+ ASSERT_EQ(list[1], 2);
+ ASSERT_EQ(list[2], 3);
+ ASSERT_EQ(list[3], 4);
+}
+
+TEST(LlvmLibcLsearchTest, SearchNonExistent) {
+ int list[4] = {1, 2, 3, 4};
+ size_t len = 3;
+ int key = 5;
+ void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+ ASSERT_EQ(static_cast<int *>(ret), &list[3]);
+ ASSERT_EQ(len, static_cast<size_t>(4));
+ ASSERT_EQ(list[0], 1);
+ ASSERT_EQ(list[1], 2);
+ ASSERT_EQ(list[2], 3);
+ ASSERT_EQ(list[3], 5);
+}
+
+TEST(LlvmLibcLsearchTest, SearchNonExistentEmpty) {
+ int list[1] = {1};
+ size_t len = 0;
+ int key = 0;
+ void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+ ASSERT_EQ(static_cast<int *>(ret), &list[0]);
+ ASSERT_EQ(len, static_cast<size_t>(1));
+ ASSERT_EQ(list[0], 0);
+}
More information about the libc-commits
mailing list