[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel std::search_n() based on __parallel_find() (PR #214069)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 5 03:54:20 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Michael G. Kazakov (mikekazakov)

<details>
<summary>Changes</summary>

This PR implements a parallel version of `std::search_n()` based on `__parallel_find()`.
The implementation is in `cpu_algos` as it's not feasible to efficiently implement it in the `default` backend.

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 and the first one found is returned.
Since it's based on `__parellel_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 29.15 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/214069.diff


14 Files Affected:

- (modified) libcxx/include/CMakeLists.txt (+1) 
- (modified) libcxx/include/__algorithm/pstl.h (+44) 
- (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 (+15) 
- (modified) libcxx/include/__pstl/backends/std_thread.h (+5) 
- (added) libcxx/include/__pstl/cpu_algos/search_n.h (+111) 
- (modified) libcxx/include/module.modulemap.in (+3) 
- (modified) libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp (+5) 
- (modified) libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp (+4) 
- (added) libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_n.pass.cpp (+160) 
- (added) libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_n_pred.pass.cpp (+164) 
- (modified) libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (+12) 


``````````diff
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 98b09b9dcf8ae..8a55e5dbfe4aa 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -698,6 +698,7 @@ set(files
   __pstl/cpu_algos/merge.h
   __pstl/cpu_algos/mismatch.h
   __pstl/cpu_algos/reverse.h
+  __pstl/cpu_algos/search_n.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..9a9eb8905f2dd 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -792,6 +792,50 @@ _LIBCPP_HIDE_FROM_ABI _ForwardOutIterator rotate_copy(
       std::move(__result));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _Size,
+          class _Tp,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator search_n(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Size __count, const _Tp& __value) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "search_n requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__search_n, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first),
+      std::move(__last),
+      std::move(__count),
+      __value,
+      equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _Size,
+          class _Tp,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator search_n(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator __first,
+    _ForwardIterator __last,
+    _Size __count,
+    const _Tp& __value,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "search_n requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__search_n, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first),
+      std::move(__last),
+      std::move(__count),
+      __value,
+      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 e83375d1c0c74..1a8f33f6beec2 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -187,6 +187,13 @@ struct __merge;
 //                       _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                       _ForwardOutIterator __result, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __search_n;
+// template <class _Policy, class _ForwardIterator, class _Size, class _T, class _Predicate>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last,
+//                       _Size __count, const _T& __value, _Predicate __pred) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __stable_sort;
 // template <class _Policy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5562c23bf09f1..1f3784e5ef399 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -94,6 +94,10 @@ namespace __pstl {
 // ------------
 // No other algorithms based on reverse
 //
+// search_n family
+// ------------------
+// No other algorithms based on search_n
+//
 // stable_sort family
 // ------------------
 // - sort
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index ee698742152f3..11df83cd9fd7a 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_n.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_n<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_search_n<__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..4294224268e44 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_n.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -117,6 +118,20 @@ struct __reverse<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __search_n<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _Size, class _Tp, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&&,
+             _ForwardIterator __first,
+             _ForwardIterator __last,
+             _Size __count,
+             const _Tp& __value,
+             _Predicate __pred) const noexcept {
+    return std::search_n(std::move(__first), std::move(__last), __count, __value, 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..850956a34d7c7 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_n.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_n<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_search_n<__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_n.h b/libcxx/include/__pstl/cpu_algos/search_n.h
new file mode 100644
index 0000000000000..b98ae33a4374a
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/search_n.h
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_N_H
+#define _LIBCPP___PSTL_CPU_ALGOS_SEARCH_N_H
+
+#include <__algorithm/search_n.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/move.h>
+#include <__utility/pair.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_n {
+  template <class _Policy, class _ForwardIterator, class _Size, class _Tp, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&&,
+             _ForwardIterator __first,
+             _ForwardIterator __last,
+             _Size __count,
+             const _Tp& __value,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+      typedef typename std::iterator_traits<_ForwardIterator>::difference_type _DifferenceType;
+      if (static_cast<_DifferenceType>(__count) <= 0) {
+        return __first; // If the count is non-positive, the first iterator is returned.
+      }
+      _DifferenceType __size = __last - __first;
+      if (__size < static_cast<_DifferenceType>(__count)) {
+        return __last; // The range is too small to contain the requested number of consecutive elements.
+      }
+      // We're only interested in the range where a potential match can start.
+      _DifferenceType __n      = __size - static_cast<_DifferenceType>(__count) + 1;
+      _ForwardIterator __last2 = __first + __n;
+      // Run a parallel chunked find_if, covering the range where a potential match can start.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first,
+          __last2,
+          [__count, &__value, &__pred](_ForwardIterator __brick_first, _ForwardIterator __brick_last) {
+            // [__brick_first, __brick_first + __count) is guaranteed to be a valid range.
+            // __brick_first can walk past __brick_last.
+            for (; __brick_first < __brick_last; ++__brick_first) {
+              if (!__pred(*__brick_first, __value)) {
+                continue;
+              }
+              // We found a start of a potential match, now check the next __count - 1 elements.
+              _ForwardIterator __match_start = __brick_first;
+              _Size __matches                = static_cast<_Size>(0);
+              while (true) {
+                if (++__matches == __count) {
+                  return __match_start; // Found a full match, return the start of the match.
+                }
+                ++__brick_first;
+                if (!__pred(*__brick_first, __value)) {
+                  break; // The match was broken, continue searching for a new potential match.
+                }
+              }
+            }
+            return __brick_last; // No matches starting in this chunk, return the end of the chunk.
+          },
+          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 == __last2) {
+        return __last; // 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_n(std::move(__first), std::move(__last), __count, __value, std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_SEARCH_N_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 41c1ac1595234..11f8d46542bc6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2433,6 +2433,9 @@ module std {
       module reverse {
         header "__pstl/cpu_algos/reverse.h"
       }
+      module search_n {
+        header "__pstl/cpu_algos/search_n.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 e61c562025fa0..0f1697c3c306c 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -217,6 +217,11 @@ 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_n(pol, non_fwd, non_fwd, 1, 1);       // expected-error@*:* {{static assertion failed: search_n}}
+    (void)std::search_n(pol, non_fwd, non_fwd, 1, 1, pred); // expected-error@*:* {{static assertion failed: search_n}}
+  }
+
   {
     (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..44e8acfcb877e 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 at +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 at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::search_n(std::execution::par, std::begin(a), std::end(a), 1, 1);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::search_n(std::execution::par, std::begin(a), std::end(a), 1, 1, pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_n.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_n.pass.cpp
new file mode 100644
index 0000000000000..b5896a05c359c
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/pstl.search_n.pass.cpp
@@ -0,0 +1,160 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ForwardIterator, class Size, class T>
+//   ForwardIterator
+//   search_n(ExecutionPolicy&& exec,
+//            ForwardIterator first, ForwardIterator last,
+//            Size count, const T& value);
+
+#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_n);
+
+static_assert(sfinae_test_search_n<int, int*, int*, int, int>);
+static_assert(!sfinae_test_search_n<std::execution::parallel_policy, int*, int*, int, int>);
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // check the return types
+      int a[]  = {0};
+      auto res = std::search_n(policy, Iter(std::begin(a)), Iter(std::begin(a)), 1, 0);
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    { // single element range with count = 1, matching
+      int a[]  = {5};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 1, 5);
+      assert(ret == Iter(std::begin(a)));
+    }
+    { // single element range with count = 1, not matching
+      int a[]  = {5};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 1, 3);
+      assert(ret == Iter(std::end(a)));
+    }
+    { // simple test - single match in the middle, count = 1
+      int a[]  = {1, 2, 3, 4, 5, 6};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 1, 3);
+      assert(ret == Iter(std::begin(a) + 2));
+    }
+    { // matching part begins at the front
+      int a[]  = {7, 7, 3, 7, 3, 6};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 2, 7);
+      assert(ret == Iter(std::begin(a)));
+    }
+    { // matching part ends at the back
+      int a[]  = {9, 3, 6, 4, 4};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 2, 4);
+      assert(ret == Iter(std::begin(a) + 3));
+    }
+    { // pattern does not match
+      int a[]  = {9, 3, 6, 4, 8};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 1, 1);
+      assert(ret == Iter(std::end(a)));
+    }
+    { // range and pattern are identical
+      int a[]  = {1, 1, 1, 1};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 4, 1);
+      assert(ret == Iter(std::begin(a)));
+    }
+    { // pattern is longer than range
+      int a[]  = {5};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 2, 5);
+      assert(ret == Iter(std::end(a)));
+    }
+    { // pattern is longer than range
+      int a[]  = {3, 3, 3};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 4, 3);
+      assert(ret == Iter(std::end(a)));
+    }
+    { // pattern has zero length
+      int a[]  = {6, 7, 8};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 0, 7);
+      assert(ret == Iter(std::begin(a)));
+    }
+    { // range has zero length
+      int a[]  = {0};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::begin(a)), 1, 1);
+      assert(ret == Iter(std::begin(a)));
+    }
+    {   // check that the first match is returned
+      { // match is at the start
+        int a[]  = {6, 6, 8, 6, 6, 8, 6, 6, 8};
+        auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 2, 6);
+        assert(ret == Iter(std::begin(a)));
+      }
+      { // match is in the middle
+        int a[]  = {6, 8, 8, 6, 6, 8, 6, 6, 8};
+        auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 2, 6);
+        assert(ret == Iter(std::begin(a) + 3));
+      }
+      { // match is at the end
+        int a[]  = {6, 6, 8, 6, 6, 8, 6, 6, 6};
+        auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 3, 6);
+        assert(ret == Iter(std::begin(a) + 6));
+      }
+      { // multiple overlapping potential matches
+        int a[]  = {3, 3, 3, 3, 3};
+        auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::end(a)), 3, 3);
+        assert(ret == Iter(std::begin(a)));
+      }
+    }
+    { // first almost matches, second is a match
+      int a[]  = {6, 6, 6, 7, 6, 6, 6, 6};
+      auto ret = std::search_n(policy, Iter(std::begin(a)), Iter(std::e...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/214069


More information about the libcxx-commits mailing list