[libc-commits] [libc] [libc][search] implement posix `lsearch` function (PR #114686)

via libc-commits libc-commits at lists.llvm.org
Sat Nov 2 15:47:40 PDT 2024


https://github.com/duncpro created https://github.com/llvm/llvm-project/pull/114686

# Changes
- Implement the POSIX [lsearch](https://man7.org/linux/man-pages/man3/lsearch.3.html) function. 

- Put a checkmark in the [posix support table docs](https://libc.llvm.org/libc_search.html) next to `lsearch`.

Also, I'm not sure how to specify a function pointer type in the [oldhdrgen spec](https://github.com/duncpro/llvm-project/blob/68377282a1e0c10cd0938a7c8604877f042c57d2/libc/spec/posix.td#L1621-L1630) files. If someone could help with that I'd appreciate it.

>From 68377282a1e0c10cd0938a7c8604877f042c57d2 Mon Sep 17 00:00:00 2001
From: Duncan <duncpro at icloud.com>
Date: Sat, 2 Nov 2024 18:18:08 -0400
Subject: [PATCH] [libc][search] implement posix `lsearch` function

---
 libc/config/linux/aarch64/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            | 12 ++++
 libc/src/search/lsearch.cpp               | 33 ++++++++++
 libc/src/search/lsearch.h                 | 20 ++++++
 libc/test/src/search/CMakeLists.txt       | 11 ++++
 libc/test/src/search/lsearch_test.cpp     | 79 +++++++++++++++++++++++
 11 files changed, 180 insertions(+), 1 deletion(-)
 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/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index b3f94a581c8ad9..51206aa2286392 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -943,6 +943,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.search.hsearch_r
     libc.src.search.insque
     libc.src.search.remque
+    libc.src.search.lsearch
 
     # threads.h entrypoints
     libc.src.threads.call_once
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 5c09edf7cfb266..34628645664f85 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -873,6 +873,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.search.hsearch_r
     libc.src.search.insque
     libc.src.search.remque
+    libc.src.search.lsearch
 
     # threads.h entrypoints
     libc.src.threads.call_once
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a2fb97d04584d5..f885d89c6606ec 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1006,6 +1006,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.search.hsearch_r
     libc.src.search.insque
     libc.src.search.remque
+    libc.src.search.lsearch
 
     # threads.h entrypoints
     libc.src.threads.call_once
diff --git a/libc/docs/libc_search.rst b/libc/docs/libc_search.rst
index 4a7ee288dd43e8..84fc9b18f2786e 100644
--- a/libc/docs/libc_search.rst
+++ b/libc/docs/libc_search.rst
@@ -43,7 +43,7 @@ hdestroy                     |check|
 hsearch                      |check|
 insque                       |check|
 lfind
-lsearch
+lsearch                      |check|
 remque                       |check|
 tdelete
 tfind
diff --git a/libc/newhdrgen/yaml/search.yaml b/libc/newhdrgen/yaml/search.yaml
index 37d2650bcf0514..af6dc0fab18594 100644
--- a/libc/newhdrgen/yaml/search.yaml
+++ b/libc/newhdrgen/yaml/search.yaml
@@ -36,6 +36,16 @@ functions:
     arguments:
       - type: ENTRY
       - type: ACTION
+  - name: lsearch
+    standards:
+      - POSIX
+    return_type: void *
+    arguments:
+      - type: void *
+      - type: void *
+      - type: size_t *
+      - type: size_t
+      - type: int(*compar)(const void *, const void *)
   - name: hsearch_r
     standards: GNUExtensions
     return_type: int
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index beede79a38ec24..448d8d2fbf17c7 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -1618,6 +1618,17 @@ def POSIX : StandardSpec<"POSIX"> {
                 ArgSpec<VoidPtr>
             ]
         >,
+        FunctionSpec<
+            "lsearch",
+            RetValSpec<VoidPtr>,
+            [
+              ArgSpec<VoidPtr>,
+              ArgSpec<VoidPtr>,
+              ArgSpec<SizeTPtr>,
+              ArgSpec<SizeTType>,
+              // TODO: Unsure how to specify int(*compar)(void *, void *)
+            ]
+        >
     ]
   >;
 
diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt
index 46ad3e33c02fa9..3208307745adb1 100644
--- a/libc/src/search/CMakeLists.txt
+++ b/libc/src/search/CMakeLists.txt
@@ -27,6 +27,18 @@ add_entrypoint_object(
     libc.include.search
 )
 
+add_entrypoint_object(
+  lsearch
+  SRCS
+    lsearch.cpp
+  HDRS
+    lsearch.h
+  DEPENDS
+    libc.include.search
+    libc.src.__support.CPP.cstddef
+    libc.src.string.memory_utils.inline_memcpy
+)
+
 add_entrypoint_object(
   hsearch
   SRCS
diff --git a/libc/src/search/lsearch.cpp b/libc/src/search/lsearch.cpp
new file mode 100644
index 00000000000000..29cc9fde279c46
--- /dev/null
+++ b/libc/src/search/lsearch.cpp
@@ -0,0 +1,33 @@
+//===-- 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/string/memory_utils/inline_memcpy.h" // inline_memcpy
+
+namespace LIBC_NAMESPACE_DECL {
+LLVM_LIBC_FUNCTION(void *, lsearch,
+                   (void *key, void *base, size_t *nmemb, size_t size,
+                    int (*compar)(void *, void *))) {
+  cpp::byte *next = reinterpret_cast<cpp::byte *>(base);
+  cpp::byte *end = next + (*nmemb * size);
+  while (next < end) {
+    if (compar(key, next) == 0) {
+      return next;
+    }
+    next += size;
+  }
+
+  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..d3902b3a2ebce0
--- /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 <search.h> // size_t
+
+namespace LIBC_NAMESPACE_DECL {
+void *lsearch(void *key, void *base, size_t *nmemb, size_t size,
+              int (*compar)(void *, 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 8a33edc4293ab2..70ac7d5a4fc859 100644
--- a/libc/test/src/search/CMakeLists.txt
+++ b/libc/test/src/search/CMakeLists.txt
@@ -25,3 +25,14 @@ add_libc_unittest(
     libc.src.search.insque
     libc.src.search.remque
 )
+
+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..2717fd6f038439
--- /dev/null
+++ b/libc/test/src/search/lsearch_test.cpp
@@ -0,0 +1,79 @@
+//===-- Unittests for lsearch ---------------------------------------------===//
+//
+// 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(void *a, void *b) {
+  return *reinterpret_cast<int *>(a) - *reinterpret_cast<int *>(b);
+}
+
+TEST(LlvmLibcLsearchTest, SearchHead) {
+  int list[4] = {1, 2, 3, 4};
+  size_t len = 3; // intentionally 3 and not all 4
+  int key = 1;
+  void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+  ASSERT_TRUE(ret == &list[0]);           // head must be returned
+  ASSERT_EQ(key, 1);                      // `key` must not be changed
+  ASSERT_EQ(len, static_cast<size_t>(3)); // `len` must not be changed
+  // `list` must not be changed
+  ASSERT_EQ(list[0], 1);
+  ASSERT_EQ(list[1], 2);
+  ASSERT_EQ(list[2], 3);
+  ASSERT_EQ(list[3], 4);
+}
+
+TEST(LlvmLibcLsearchTest, SearchMiddle) {
+  int list[4] = {1, 2, 3, 4};
+  size_t len = 3; // intentionally 3 and not all 4
+  int key = 2;
+  void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+  ASSERT_TRUE(ret == &list[1]); // ptr to second element must be returned
+  ASSERT_EQ(key, 2);            // `key` must not be changed
+  ASSERT_EQ(len, static_cast<size_t>(3)); // `len` must not be changed
+  // `list` must not be changed
+  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; // intentionally 3 and not all 4
+  int key = 3;
+  void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+  ASSERT_TRUE(ret == &list[2]); // ptr to last element must be returned
+  ASSERT_EQ(key, 3);            // `key` must not be changed
+  ASSERT_EQ(len, static_cast<size_t>(3)); // `len` must not be changed
+  // `list` must not be changed
+  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; // intentionally 3 and not all 4
+  int key = 5;
+  void *ret = LIBC_NAMESPACE::lsearch(&key, list, &len, sizeof(int), compar);
+
+  ASSERT_TRUE(ret == &list[3]);           // ptr past tail must be returned
+  ASSERT_EQ(key, 5);                      // `key` must not be changed
+  ASSERT_EQ(len, static_cast<size_t>(4)); // `len` must be incremented one
+  // `list` must not be changed
+  ASSERT_EQ(list[0], 1);
+  ASSERT_EQ(list[1], 2);
+  ASSERT_EQ(list[2], 3);
+  // `5` must be appended to list (replacing the dummy 4)
+  ASSERT_EQ(list[3], 5);
+}



More information about the libc-commits mailing list