diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 68030f7f1775..f136711b5409 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -26,6 +26,9 @@ set(TARGET_LIBC_ENTRYPOINTS # errno.h entrypoints libc.src.errno.errno + # search.h entrypoints + libc.src.search.lfind + # setjmp.h entrypoints libc.src.setjmp.longjmp libc.src.setjmp.setjmp diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt index 5894b591072e..08c36ce83967 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -26,6 +26,9 @@ set(TARGET_LIBC_ENTRYPOINTS # errno.h entrypoints libc.src.errno.errno + # search.h entrypoints + libc.src.search.lfind + # string.h entrypoints libc.src.string.bcmp libc.src.string.bcopy diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index 2d5dbeff4857..13860015ae58 100644 --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -20,6 +20,9 @@ set(TARGET_LIBC_ENTRYPOINTS # errno.h entrypoints libc.src.errno.errno + # search.h entrypoints + libc.src.search.lfind + # string.h entrypoints libc.src.string.bcmp libc.src.string.bcopy diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt index 49c19571ac41..64eeed18f381 100644 --- a/libc/config/darwin/x86_64/entrypoints.txt +++ b/libc/config/darwin/x86_64/entrypoints.txt @@ -17,6 +17,9 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.ctype.tolower libc.src.ctype.toupper + # search.h entrypoints + libc.src.search.lfind + # string.h entrypoints libc.src.string.bcmp libc.src.string.bzero diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 571527426de3..74ca3742977a 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -960,6 +960,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.search.hsearch libc.src.search.hsearch_r libc.src.search.insque + libc.src.search.lfind libc.src.search.remque # threads.h entrypoints diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 383bf8c8e528..b4f08cde6df4 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -20,6 +20,9 @@ set(TARGET_LIBC_ENTRYPOINTS # errno.h entrypoints libc.src.errno.errno + # search.h entrypoints + libc.src.search.lfind + # string.h entrypoints libc.src.string.bcmp libc.src.string.bcopy diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 8108ac55838d..d63c5d651cc9 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -885,6 +885,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.search.hsearch libc.src.search.hsearch_r libc.src.search.insque + libc.src.search.lfind 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 bc2f7931d3f3..d4f552617c27 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1025,6 +1025,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.search.hsearch libc.src.search.hsearch_r libc.src.search.insque + libc.src.search.lfind libc.src.search.remque # threads.h entrypoints diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 8f0b50bcc83e..5ffd32373148 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -17,6 +17,9 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.ctype.tolower libc.src.ctype.toupper + # search.h entrypoints + libc.src.search.lfind + # string.h entrypoints libc.src.string.bcmp libc.src.string.bcopy diff --git a/libc/docs/libc_search.rst b/libc/docs/libc_search.rst index 4a7ee288dd43..774622d1e66c 100644 --- a/libc/docs/libc_search.rst +++ b/libc/docs/libc_search.rst @@ -42,7 +42,7 @@ hcreate |check| hdestroy |check| hsearch |check| insque |check| -lfind +lfind |check| lsearch remque |check| tdelete diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 5deb5258d532..30d0b71d88cc 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -254,6 +254,7 @@ add_header_macro( .llvm-libc-types.ENTRY .llvm-libc-types.struct_hsearch_data .llvm-libc-types.size_t + .llvm-libc-types.__lsearchcompare_t ) add_header_macro( diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index 836e8a507bd6..a104ab786896 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -3,6 +3,7 @@ add_header(size_t HDR size_t.h) add_header(ssize_t HDR ssize_t.h) add_header(__atfork_callback_t HDR __atfork_callback_t.h) add_header(__bsearchcompare_t HDR __bsearchcompare_t.h) +add_header(__lsearchcompare_t HDR __lsearchcompare_t.h) add_header(__call_once_func_t HDR __call_once_func_t.h) add_header(__exec_argv_t HDR __exec_argv_t.h) add_header(__exec_envp_t HDR __exec_envp_t.h) diff --git a/libc/include/llvm-libc-types/__lsearchcompare_t.h b/libc/include/llvm-libc-types/__lsearchcompare_t.h new file mode 100644 index 000000000000..08dc2db274d0 --- /dev/null +++ b/libc/include/llvm-libc-types/__lsearchcompare_t.h @@ -0,0 +1,14 @@ +//===-- Definition of type __lsearchcompare_t -----------------------------===// +// +// 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_TYPES___LSEARCHCOMPARE_T_H +#define LLVM_LIBC_TYPES___LSEARCHCOMPARE_T_H + +typedef int (*__lsearchcompare_t)(const void *, const void *); + +#endif // LLVM_LIBC_TYPES___LSEARCHCOMPARE_T_H diff --git a/libc/newhdrgen/yaml/search.yaml b/libc/newhdrgen/yaml/search.yaml index 37d2650bcf05..a0c73bc679d8 100644 --- a/libc/newhdrgen/yaml/search.yaml +++ b/libc/newhdrgen/yaml/search.yaml @@ -4,6 +4,7 @@ types: - type_name: struct_hsearch_data - type_name: ENTRY - type_name: ACTION + - type_name: __lsearchcompare_t enums: [] objects: [] functions: @@ -57,3 +58,13 @@ functions: return_type: void arguments: - type: void * + - name: lfind + standards: + - POSIX + return_type: void * + arguments: + - type: const void * + - type: const void * + - type: size_t * + - type: size_t + - type: __lsearchcompare_t diff --git a/libc/spec/posix.td b/libc/spec/posix.td index beede79a38ec..d4712dc0c5d7 100644 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -100,6 +100,9 @@ def StructStatvfs : NamedType<"struct statvfs">; def StructStatvfsPtr : PtrType; def RestrictedStructStatvfsPtr : RestrictedPtrType; +// The function pointer type for the predicate for lsearch, lfind +def LSearchCompareT : NamedType<"__lsearchcompare_t">; + def POSIX : StandardSpec<"POSIX"> { PtrType CharPtr = PtrType; RestrictedPtrType RestrictedCharPtr = RestrictedPtrType; @@ -1618,6 +1621,17 @@ def POSIX : StandardSpec<"POSIX"> { ArgSpec ] >, + FunctionSpec< + "lfind", + RetValSpec, + [ + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec + ] + > ] >; diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt index 46ad3e33c02f..497657f40f2f 100644 --- a/libc/src/search/CMakeLists.txt +++ b/libc/src/search/CMakeLists.txt @@ -98,3 +98,15 @@ add_entrypoint_object( libc.include.search libc.src.__support.intrusive_list ) + +add_entrypoint_object( + lfind + SRCS + lfind.cpp + HDRS + lfind.h + DEPENDS + libc.include.search + libc.src.__support.CPP.cstddef + libc.src.__support.memory_size +) diff --git a/libc/src/search/lfind.cpp b/libc/src/search/lfind.cpp new file mode 100644 index 000000000000..c8bf07de0b90 --- /dev/null +++ b/libc/src/search/lfind.cpp @@ -0,0 +1,35 @@ +//===-- Implementation of lfind -------------------------------*- 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/lfind.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" + +namespace LIBC_NAMESPACE_DECL { +LLVM_LIBC_FUNCTION(void *, lfind, + (const void *key, const 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; + + const cpp::byte *next = reinterpret_cast(base); + const cpp::byte *end = next + byte_len; + for (; next < end; next += size) + if (compar(key, next) == 0) + return const_cast(next); + return nullptr; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/search/lfind.h b/libc/src/search/lfind.h new file mode 100644 index 000000000000..2eddb027d885 --- /dev/null +++ b/libc/src/search/lfind.h @@ -0,0 +1,20 @@ +//===-- Implementation header for lfind -------------------------*- 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_LFIND_H +#define LLVM_LIBC_SRC_SEARCH_LFIND_H + +#include "src/__support/macros/config.h" +#include // size_t + +namespace LIBC_NAMESPACE_DECL { +void *lfind(const void *key, const void *base, size_t *nmemb, size_t size, + int (*compar)(const void *, const void *)); +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SEARCH_LFIND_H diff --git a/libc/test/src/search/CMakeLists.txt b/libc/test/src/search/CMakeLists.txt index 8a33edc4293a..a1f9aac2094c 100644 --- a/libc/test/src/search/CMakeLists.txt +++ b/libc/test/src/search/CMakeLists.txt @@ -25,3 +25,13 @@ add_libc_unittest( libc.src.search.insque libc.src.search.remque ) + +add_libc_unittest( + lfind_test + SUITE + libc_search_unittests + SRCS + lfind_test.cpp + DEPENDS + libc.src.search.lfind +) diff --git a/libc/test/src/search/lfind_test.cpp b/libc/test/src/search/lfind_test.cpp new file mode 100644 index 000000000000..00384f7eec14 --- /dev/null +++ b/libc/test/src/search/lfind_test.cpp @@ -0,0 +1,46 @@ +//===-- 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/lfind.h" +#include "test/UnitTest/Test.h" + +int compar(const void *a, const void *b) { + return *reinterpret_cast(a) != *reinterpret_cast(b); +} + +TEST(LlvmLibcLfindTest, SearchHead) { + int list[3] = {1, 2, 3}; + size_t len = 3; + int key = 1; + void *ret = LIBC_NAMESPACE::lfind(&key, list, &len, sizeof(int), compar); + ASSERT_TRUE(ret == &list[0]); +} + +TEST(LlvmLibcLfindTest, SearchMiddle) { + int list[3] = {1, 2, 3}; + size_t len = 3; + int key = 2; + void *ret = LIBC_NAMESPACE::lfind(&key, list, &len, sizeof(int), compar); + ASSERT_TRUE(ret == &list[1]); +} + +TEST(LlvmLibcLfindTest, SearchTail) { + int list[3] = {1, 2, 3}; + size_t len = 3; + int key = 3; + void *ret = LIBC_NAMESPACE::lfind(&key, list, &len, sizeof(int), compar); + ASSERT_TRUE(ret == &list[2]); +} + +TEST(LlvmLibcLfindTest, SearchNonExistent) { + int list[3] = {1, 2, 3}; + size_t len = 3; + int key = 5; + void *ret = LIBC_NAMESPACE::lfind(&key, list, &len, sizeof(int), compar); + ASSERT_TRUE(ret == nullptr); +}