From b11ed8107af59ce99419a65eb7e3be43124a531c Mon Sep 17 00:00:00 2001 From: Duncan Date: Tue, 19 Nov 2024 15:10:22 -0500 Subject: [PATCH 1/2] [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 64eeed18f3819..5efdf7ee086d7 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 74ca3742977a5..a64fe17f4cacb 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 31d81de06fb6b..259fa1bd9d984 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 5419462d4f5b3..e1c7ecc89bc3e 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 957e28bd66cc4..7d3a4237be613 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 774622d1e66c3..7d31181427507 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 a0c73bc679d81..38b950fc8a231 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 e354deef340f1..5ba334888bd5f 100644 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -1632,6 +1632,17 @@ def POSIX : StandardSpec<"POSIX"> { ArgSpec, ArgSpec ] + >, + FunctionSpec< + "lsearch", + RetValSpec, + [ + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec + ] > ] >; diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt index 497657f40f2f0..d78ea062342a1 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 0000000000000..a9f0aa3e7530f --- /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(const_cast(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 0000000000000..c95e01d396cd0 --- /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 // 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 a1f9aac2094c9..9f34d4d3fd259 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 0000000000000..ed3d51f4d0fd4 --- /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(const void *a, const void *b) { + return *reinterpret_cast(a) != *reinterpret_cast(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(ret), &list[0]); + ASSERT_EQ(len, static_cast(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(ret), &list[1]); + ASSERT_EQ(len, static_cast(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(ret), &list[2]); + ASSERT_EQ(len, static_cast(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(ret), &list[3]); + ASSERT_EQ(len, static_cast(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(ret), &list[0]); + ASSERT_EQ(len, static_cast(1)); + ASSERT_EQ(list[0], 0); +} From 47f780083d8041a4cddffc484294067a22bf6e58 Mon Sep 17 00:00:00 2001 From: Duncan Date: Wed, 27 Nov 2024 12:26:36 -0500 Subject: [PATCH 2/2] fix list integrity assertions in SearchHead --- libc/test/src/search/lsearch_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/test/src/search/lsearch_test.cpp b/libc/test/src/search/lsearch_test.cpp index ed3d51f4d0fd4..4e8b687eaa9fe 100644 --- a/libc/test/src/search/lsearch_test.cpp +++ b/libc/test/src/search/lsearch_test.cpp @@ -21,10 +21,10 @@ TEST(LlvmLibcLsearchTest, SearchHead) { ASSERT_EQ(static_cast(ret), &list[0]); ASSERT_EQ(len, static_cast(3)); + ASSERT_EQ(list[0], 1); ASSERT_EQ(list[1], 2); ASSERT_EQ(list[2], 3); ASSERT_EQ(list[3], 4); - ASSERT_EQ(list[3], 4); } TEST(LlvmLibcLsearchTest, SearchMiddle) {