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

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 24 07:57:32 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::find_end()` based on `__parallel_find()`.
It follows the same approach as with `std::search()` and `std::search_n()`:

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::find_end()` and the last 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 39.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218321.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/find_end.h (+102) 
- (modified) libcxx/include/module.modulemap.in (+3) 
- (modified) libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp (+9) 
- (modified) libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp (+4) 
- (added) libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp (+268) 
- (added) libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end_pred.pass.cpp (+276) 
- (modified) libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (+14) 


``````````diff
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d717a7354c62b..b5150618ce1bc 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -695,6 +695,7 @@ set(files
   __pstl/cpu_algos/any_of.h
   __pstl/cpu_algos/cpu_traits.h
   __pstl/cpu_algos/fill.h
+  __pstl/cpu_algos/find_end.h
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index a8ef6ddae9b63..2aba027a0ea48 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -419,6 +419,54 @@ fill_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, const _
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), __value);
 }
 
+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 find_end(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_end requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_end requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_end, __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 find_end(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_end requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_end requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_end, __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 _ForwardIterator,
           class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 45e570c51aed9..736de86ca0189 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -116,6 +116,13 @@ struct __find_first_of;
 // operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __find_end;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
 // template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index ecf5b3bc3d9eb..1b434fdabfff2 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -60,6 +60,10 @@ namespace __pstl {
 //
 // This backend implements all the PSTL algorithms based on the following basis operations:
 //
+// find_end family
+// ------------------
+// No other algorithms based on find_end
+//
 // find_if family
 // --------------
 // - find
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index c5efbd46298fb..48970c8c3402d 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/any_of.h>
 #include <__pstl/cpu_algos/cpu_traits.h>
 #include <__pstl/cpu_algos/fill.h>
+#include <__pstl/cpu_algos/find_end.h>
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
@@ -362,6 +363,10 @@ struct __cpu_traits<__libdispatch_backend_tag> {
 };
 
 // Mandatory implementations of the computational basis
+template <class _ExecutionPolicy>
+struct __find_end<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_find_end<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __find_if<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_find_if<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index c67f0b3331ac2..b64dba7ec0d3a 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_SERIAL_H
 #define _LIBCPP___PSTL_BACKENDS_SERIAL_H
 
+#include <__algorithm/find_end.h>
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
@@ -50,6 +51,21 @@ namespace __pstl {
 //       if we end up running serially anyways.
 //
 
+template <class _ExecutionPolicy>
+struct __find_end<__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::find_end(
+        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Pred>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index 06beb04e4d885..9beffc6c304f1 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -16,6 +16,7 @@
 #include <__pstl/cpu_algos/any_of.h>
 #include <__pstl/cpu_algos/cpu_traits.h>
 #include <__pstl/cpu_algos/fill.h>
+#include <__pstl/cpu_algos/find_end.h>
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
@@ -93,6 +94,10 @@ struct __cpu_traits<__std_thread_backend_tag> {
 };
 
 // Mandatory implementations of the computational basis
+template <class _ExecutionPolicy>
+struct __find_end<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_find_end<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __find_if<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_find_if<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/find_end.h b/libcxx/include/__pstl/cpu_algos/find_end.h
new file mode 100644
index 0000000000000..5beef0fc068e9
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/find_end.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_FIND_END_H
+#define _LIBCPP___PSTL_CPU_ALGOS_FIND_END_H
+
+#include <__algorithm/find_end.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_find_end {
+  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 __last1; // If the needle length is zero, the last 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::find_end to find a full match, which can go beyond __brick_last.
+            _ForwardIterator1 __brick_last_uncropped = __brick_last + __crop;
+            // Run a serial std::find_end inside each of the chunks in parallel.
+            _ForwardIterator1 __ret = std::find_end(__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;
+          },
+          greater<>{}, // `greater` here means the highest index among the matches
+          false        // `false` here means we want the last match, not the first
+      );
+      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::find_end(
+          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_FIND_END_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 78f4e08d9c282..eceadcfae2382 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2428,6 +2428,9 @@ module std {
         header "__pstl/cpu_algos/fill.h"
         export std_core.utility_core.empty
       }
+      module find_end {
+        header "__pstl/cpu_algos/find_end.h"
+      }
       module find_if {
         header "__pstl/cpu_algos/find_if.h"
       }
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 7e9fa5cfacc30..3c55c872a6e76 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -99,6 +99,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::find_if_not(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_if_not}}
   }
 
+  {
+    (void)std::find_end(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_end}}
+    (void)std::find_end(
+        pol, it, it, non_fwd, non_fwd, pred);           // expected-error@*:* {{static assertion failed: find_end}}
+    (void)std::find_end(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: find_end}}
+    (void)std::find_end(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: find_end}}
+  }
+
   {
     (void)std::find_first_of(
         pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 61758770102f3..8398bddade79f 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -42,6 +42,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::equal(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::find_end(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::find_end(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::find_if(std::execution::par, std::begin(a), std::end(a), pred);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find_if_not(std::execution::par, std::begin(a), std::end(a), pred);
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp
new file mode 100644
index 0000000000000..7b7c6acf3d95d
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp
@@ -0,0 +1,268 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//   find_end(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(find_end);
+
+static_assert(sfinae_test_find_end<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_end<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::find_end(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::find_end(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::find_end(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::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::begin(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Both single element, same
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::find_end(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::find_end(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 the beginning
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {0};
+      auto res =
+          std::find_end(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 the middle
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {1};
+      auto res =
+          std::find_end(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 the end
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {5};
+      auto res =
+          std::find_end(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 the beginning
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {0, 1, 2};
+      auto res =
+          std::find_end(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 the middle
+      int a[] = {...
[truncated]

``````````

</details>


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


More information about the libcxx-commits mailing list