Skip to content

[libc++][pstl] Implementation of parallel std::search() based on __parallel_find() - #215119

Merged
mikekazakov merged 6 commits into
llvm:mainfrom
mikekazakov:search
Aug 13, 2026
Merged

[libc++][pstl] Implementation of parallel std::search() based on __parallel_find()#215119
mikekazakov merged 6 commits into
llvm:mainfrom
mikekazakov:search

Conversation

@mikekazakov

@mikekazakov mikekazakov commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This PR implements a parallel version of std::search() based on __parallel_find().
It follows the same approach as with std::search_n() in #214069:

The algorithm crops the input range to a range where a potential match can start and runs a chunked parallel find on the cropped range.
Inside each chunk potential matches are looked for using the serial std::search() and the first one found is returned.
Since it's based on __parallel_find(), the algorithm supports early termination.

Included tests check that:

  • Semantics of the iterator-only function is correct.
  • Semantics of the predicated function is correct.
  • The functions correctly SFINAE out when the first argument is not an execution policy.
  • The nodiscard policy is followed.
  • The noexcept policy is followed.
  • static_assert verifies iterators' categories.

Part of #99938.

@mikekazakov
mikekazakov marked this pull request as ready for review August 10, 2026 23:14
@mikekazakov
mikekazakov requested a review from a team as a code owner August 10, 2026 23:14
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Aug 10, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-libcxx

Author: Michael G. Kazakov (mikekazakov)

Changes

This PR implements a parallel version of std::search() based on __parallel_find().
It follows the same approach as with std::search_n() in #214069:

The algorithm crops the input range to a range where a potential match can start and runs a chunked parallel find on the cropped range.
Inside each chunk potential matches are looked for using the serial std::search() and the first one found is returned.
Since it's based on __parallel_find(), the algorithm supports early termination.

Included tests check that:

  • Semantics of the iterator-only function is correct.
  • Semantics of the predicated function is correct.
  • The functions correctly SFINAE out when the first argument is not an execution policy.
  • The nodiscard policy is followed.
  • The noexcept policy is followed.
  • static_assert verifies iterators' categories.

Part of #99938.


Patch is 38.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/215119.diff

14 Files Affected:

  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__algorithm/pstl.h (+48)
  • (modified) libcxx/include/__pstl/backend_fwd.h (+7)
  • (modified) libcxx/include/__pstl/backends/default.h (+4)
  • (modified) libcxx/include/__pstl/backends/libdispatch.h (+5)
  • (modified) libcxx/include/__pstl/backends/serial.h (+16)
  • (modified) libcxx/include/__pstl/backends/std_thread.h (+5)
  • (added) libcxx/include/__pstl/cpu_algos/search.h (+102)
  • (modified) libcxx/include/module.modulemap.in (+3)
  • (modified) libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp (+8)
  • (modified) libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp (+4)
  • (added) libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search.pass.cpp (+262)
  • (added) libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_pred.pass.cpp (+269)
  • (modified) libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (+14)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d24ae7a73a825..07513bc317a60 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -699,6 +699,7 @@ set(files
   __pstl/cpu_algos/merge.h
   __pstl/cpu_algos/mismatch.h
   __pstl/cpu_algos/reverse.h
+  __pstl/cpu_algos/search.h
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index d27044ecdc8b9..15344bc60a59b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -792,6 +792,54 @@ _LIBCPP_HIDE_FROM_ABI _ForwardOutIterator rotate_copy(
       std::move(__result));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1
+search(_ExecutionPolicy&& __policy,
+       _ForwardIterator1 __first1,
+       _ForwardIterator1 __last1,
+       _ForwardIterator2 __first2,
+       _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "search requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "search requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__search, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1
+search(_ExecutionPolicy&& __policy,
+       _ForwardIterator1 __first1,
+       _ForwardIterator1 __last1,
+       _ForwardIterator2 __first2,
+       _ForwardIterator2 __last2,
+       _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "search requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "search requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__search, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _RandomAccessIterator,
           class _Comp,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index c5a219fb59db2..a2fb13a8f0c01 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -140,6 +140,13 @@ struct __fill_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Tp const& __value) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __search;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _BinaryPredicate __pred) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __replace;
 // template <class _Policy, class _ForwardIterator, class _Tp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 43bac357efd95..093ca2cd56b70 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -98,6 +98,10 @@ namespace __pstl {
 // ------------
 // No other algorithms based on reverse
 //
+// search family
+// ------------
+// No other algorithms based on search
+//
 // stable_sort family
 // ------------------
 // - sort
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index ee698742152f3..2e4eef5de033a 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -42,6 +42,7 @@
 #include <__pstl/cpu_algos/merge.h>
 #include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/reverse.h>
+#include <__pstl/cpu_algos/search.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -380,6 +381,10 @@ template <class _ExecutionPolicy>
 struct __reverse<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_reverse<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __search<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_search<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 13c515b356608..4025d3d7d598e 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -15,6 +15,7 @@
 #include <__algorithm/merge.h>
 #include <__algorithm/mismatch.h>
 #include <__algorithm/reverse.h>
+#include <__algorithm/search.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -117,6 +118,21 @@ struct __reverse<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __search<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _BinaryPredicate __pred) const noexcept {
+    return std::search(
+        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index d0b15c8ed1f00..2e8b64fd344be 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -21,6 +21,7 @@
 #include <__pstl/cpu_algos/merge.h>
 #include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/reverse.h>
+#include <__pstl/cpu_algos/search.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -111,6 +112,10 @@ template <class _ExecutionPolicy>
 struct __reverse<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_reverse<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __search<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_search<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/search.h b/libcxx/include/__pstl/cpu_algos/search.h
new file mode 100644
index 0000000000000..e74f9bebbe3ad
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/search.h
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 _LIBCPP___PSTL_CPU_ALGOS_SEARCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_SEARCH_H
+
+#include <__algorithm/search.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__optional/nullopt_t.h>
+#include <__optional/optional.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/convert_to_integral.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_search {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _BinaryPredicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      typedef typename std::iterator_traits<_ForwardIterator1>::difference_type _DifferenceType;
+      _DifferenceType __size2 = __last2 - __first2; // The length of the needle to search for.
+      if (__size2 == 0) {
+        return __first1; // If the needle length is zero, the first iterator is returned.
+      }
+      _DifferenceType __size1 = __last1 - __first1;
+      if (__size1 < __size2) {
+        return __last1; // The range is too small to contain the requested number of consecutive elements.
+      }
+      // Calculate the length of the tail where a potential match cannot start by definition.
+      _DifferenceType __crop = __size2 - 1;
+      // We're only interested in the range where a potential match can start: [first, last - crop)
+      _ForwardIterator1 __last1_cropped = __last1 - __crop;
+      // Run a parallel chunked find_if, covering the range where a potential match can start.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __last1_cropped,
+          [__first2, __last2, __crop, &__pred](_ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last) {
+            // Uncrop the range to allow std::search to find a full match, which can go beyond __brick_last.
+            _ForwardIterator1 __brick_last_uncropped = __brick_last + __crop;
+            // Run a serial std::search inside each of the chunks in parallel.
+            _ForwardIterator1 __ret = std::search(__brick_first, __brick_last_uncropped, __first2, __last2, __pred);
+            // The returned iterator is either a match inside [__brick_first, __brick_last) or a miss encoded as
+            // __brick_last_uncropped. Return the miss as __brick_last to conform to expectations of __parallel_find().
+            return __ret == __brick_last_uncropped ? __brick_last : __ret;
+          },
+          less<>{}, // `less` here means the lowest index among the matches
+          true      // `true` here means we want the first match, not the last
+      );
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (*__res == __last1_cropped) {
+        return __last1; // No match was found in the range.
+      }
+      return *__res; // Return the successful match.
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      return std::search(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_SEARCH_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index a5eac9fc149f0..d774df7458898 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2437,6 +2437,9 @@ module std {
       module reverse {
         header "__pstl/cpu_algos/reverse.h"
       }
+      module search {
+        header "__pstl/cpu_algos/search.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 2d510be58e164..f8cae6ee6d656 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -223,6 +223,14 @@ void f(non_forward_iterator non_fwd,
     (void)std::rotate_copy(pol, it, it, it, non_output); // expected-error@*:* {{static assertion failed: rotate_copy}}
   }
 
+  {
+    (void)std::search(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: search}}
+    (void)std::search(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: search}}
+
+    (void)std::search(pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: search}}
+    (void)std::search(pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: search}}
+  }
+
   {
     (void)std::sort(pol, non_fwd, non_fwd);       // expected-error@*:* {{static assertion failed: sort}}
     (void)std::sort(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: sort}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 37e843eb90fe1..766df3cc8bc4e 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -83,4 +83,8 @@ void test() {
   std::lexicographical_compare(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
   // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::lexicographical_compare(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::search(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::search(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search.pass.cpp
new file mode 100644
index 0000000000000..cc4abf51d23c4
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search.pass.cpp
@@ -0,0 +1,262 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
+//   ForwardIterator1
+//   search(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1, ForwardIterator1 last1,
+//            ForwardIterator2 first2, ForwardIterator2 last2);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(search);
+
+static_assert(sfinae_test_search<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_search<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      static_assert(std::is_same_v<decltype(res), Iter1>);
+    }
+    { // Empty haystack, empty needle
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::begin(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Empty haystack, non-empty needle
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Non-empty haystack, empty needle
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::begin(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Both single element, same
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Both single element, different
+      int a[] = {0};
+      int b[] = {1};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle found at beginning
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {0};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Needle found in middle
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {1};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 1));
+    }
+    { // Needle found at end
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {5};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a) - 1));
+    }
+    { // Multiple element needle found at beginning
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {0, 1, 2, 3, 4, 5};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Multiple element needle found in middle
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {2};
+      auto res =
+          std::search(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 2));
+    }
+    { // Multiple element ne...
[truncated]

@mikekazakov mikekazakov self-assigned this Aug 12, 2026

@ldionne ldionne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with minor suggestion on the test. This is very similar to search_n on which we spent a lot of time, so this is straightforward to review. Thanks!

Comment thread libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_pred.pass.cpp Outdated
@mikekazakov
mikekazakov merged commit f84556d into llvm:main Aug 13, 2026
83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants