[libcxx-commits] [libcxx] 06c6a50 - [libc++] Optimize search_n (#171389)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 8 02:26:12 PST 2026


Author: Nikolas Klauser
Date: 2026-01-08T11:26:08+01:00
New Revision: 06c6a5020fa00bbedc38e07c91340f03bbaa2880

URL: https://github.com/llvm/llvm-project/commit/06c6a5020fa00bbedc38e07c91340f03bbaa2880
DIFF: https://github.com/llvm/llvm-project/commit/06c6a5020fa00bbedc38e07c91340f03bbaa2880.diff

LOG: [libc++] Optimize search_n (#171389)

This changes the algorithm to more efficiently skip ranges which cannot
match the needle for random access iterators. Specifically, we now
search for a mismatching element from the back of the subrange we want
to check. When a mismatch occurs we can directly start one after the
mismatched element, since there cannot possibly be a matching subrange
starting between the start of the subrange we checked and the mismatched
element (since all elements have to be equal). The algorithm also
remembers the subrange which was already match as being equal and
doesn't try to compare it a second time, reducing the time spent in case
of a match.

```
Benchmark                                                       old             new    Difference    % Difference
---------------------------------------------------  --------------  --------------  ------------  --------------
rng::search_n(deque<int>)_(no_match)/1000                    458.33           14.22       -444.11         -96.90%
rng::search_n(deque<int>)_(no_match)/1024                    456.17           13.89       -442.28         -96.95%
rng::search_n(deque<int>)_(no_match)/1048576              453420.38           17.69    -453402.69        -100.00%
rng::search_n(deque<int>)_(no_match)/8192                   3566.08           17.60      -3548.49         -99.51%
rng::search_n(deque<int>,_pred)_(no_match)/1000              597.88           15.25       -582.63         -97.45%
rng::search_n(deque<int>,_pred)_(no_match)/1024              608.42           15.39       -593.03         -97.47%
rng::search_n(deque<int>,_pred)_(no_match)/1048576        594533.99           18.91    -594515.08        -100.00%
rng::search_n(deque<int>,_pred)_(no_match)/8192             4670.23           18.88      -4651.35         -99.60%
rng::search_n(list<int>)_(no_match)/1000                     733.72          730.22         -3.50          -0.48%
rng::search_n(list<int>)_(no_match)/1024                     759.93          753.10         -6.84          -0.90%
rng::search_n(list<int>)_(no_match)/1048576               833841.54       813483.75     -20357.79          -2.44%
rng::search_n(list<int>)_(no_match)/8192                    8352.18         8417.31         65.14           0.78%
rng::search_n(list<int>,_pred)_(no_match)/1000               776.79          789.72         12.93           1.66%
rng::search_n(list<int>,_pred)_(no_match)/1024               788.42          806.70         18.28           2.32%
rng::search_n(list<int>,_pred)_(no_match)/1048576         955536.40       982976.81      27440.41           2.87%
rng::search_n(list<int>,_pred)_(no_match)/8192              8874.02         8915.18         41.16           0.46%
rng::search_n(vector<int>)_(no_match)/1000                   212.69            3.79       -208.90         -98.22%
rng::search_n(vector<int>)_(no_match)/1024                   219.67            3.70       -215.96         -98.31%
rng::search_n(vector<int>)_(no_match)/1048576             209622.54            3.67    -209618.87        -100.00%
rng::search_n(vector<int>)_(no_match)/8192                  1643.80            3.83      -1639.98         -99.77%
rng::search_n(vector<int>,_pred)_(no_match)/1000             461.93            7.55       -454.38         -98.36%
rng::search_n(vector<int>,_pred)_(no_match)/1024             472.43            7.74       -464.69         -98.36%
rng::search_n(vector<int>,_pred)_(no_match)/1048576       546180.29            8.71    -546171.58        -100.00%
rng::search_n(vector<int>,_pred)_(no_match)/8192            3786.26            7.88      -3778.38         -99.79%
std::search_n(deque<int>)_(no_match)/1000                    455.53           14.19       -441.34         -96.88%
std::search_n(deque<int>)_(no_match)/1024                    459.79           13.98       -445.81         -96.96%
std::search_n(deque<int>)_(no_match)/1048576              449780.32           17.99    -449762.33        -100.00%
std::search_n(deque<int>)_(no_match)/8192                   3508.55           17.97      -3490.58         -99.49%
std::search_n(deque<int>,_pred)_(no_match)/1000              571.53           17.16       -554.37         -97.00%
std::search_n(deque<int>,_pred)_(no_match)/1024              584.43           17.09       -567.34         -97.08%
std::search_n(deque<int>,_pred)_(no_match)/1048576        581418.31           19.16    -581399.15        -100.00%
std::search_n(deque<int>,_pred)_(no_match)/8192             4661.97           19.36      -4642.61         -99.58%
std::search_n(list<int>)_(no_match)/1000                     722.45          710.39        -12.06          -1.67%
std::search_n(list<int>)_(no_match)/1024                     748.50          727.08        -21.42          -2.86%
std::search_n(list<int>)_(no_match)/1048576               821655.28       784520.12     -37135.16          -4.52%
std::search_n(list<int>)_(no_match)/8192                    7941.73         8002.05         60.32           0.76%
std::search_n(list<int>,_pred)_(no_match)/1000               766.59          786.31         19.72           2.57%
std::search_n(list<int>,_pred)_(no_match)/1024               785.92          804.43         18.51           2.35%
std::search_n(list<int>,_pred)_(no_match)/1048576         948252.76       969125.41      20872.65           2.20%
std::search_n(list<int>,_pred)_(no_match)/8192              8658.99         8825.71        166.72           1.93%
std::search_n(vector<int>)_(no_match)/1000                   210.36            3.47       -206.89         -98.35%
std::search_n(vector<int>)_(no_match)/1024                   217.60            4.13       -213.47         -98.10%
std::search_n(vector<int>)_(no_match)/1048576             209386.43            3.51    -209382.92        -100.00%
std::search_n(vector<int>)_(no_match)/8192                  1643.79            3.50      -1640.29         -99.79%
std::search_n(vector<int>,_pred)_(no_match)/1000             460.88            5.44       -455.45         -98.82%
std::search_n(vector<int>,_pred)_(no_match)/1024             475.36            5.43       -469.93         -98.86%
std::search_n(vector<int>,_pred)_(no_match)/1048576       682722.75            7.15    -682715.60        -100.00%
std::search_n(vector<int>,_pred)_(no_match)/8192            3779.95            5.43      -3774.52         -99.86%
Geomean                                                     4956.15           87.96      -4868.19         -98.23%
```

Fixes #129327

Added: 
    

Modified: 
    libcxx/docs/ReleaseNotes/22.rst
    libcxx/include/__algorithm/ranges_search_n.h
    libcxx/include/__algorithm/search_n.h
    libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.search/ranges.search_n.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.search/search_n.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 3e2b3e6633bb1..39e667f98bc65 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -95,6 +95,7 @@ Improvements and New Features
 - ``std::for_each`` and ``ranges::for_each`` have been optimized to iterate more efficiently over the associative
   containers, resulting in performance improvements of up to 2x.
 
+- The performance fo ``search_n`` has been significantly improved.
 - The ``num_get::do_get`` integral overloads have been optimized, resulting in a performance improvement of up to 2.8x.
 
 - The performance of ``std::align`` has been improved by making it an inline function, which allows the compiler to

diff  --git a/libcxx/include/__algorithm/ranges_search_n.h b/libcxx/include/__algorithm/ranges_search_n.h
index 81b568c0965fd..746bfcc3d1a8f 100644
--- a/libcxx/include/__algorithm/ranges_search_n.h
+++ b/libcxx/include/__algorithm/ranges_search_n.h
@@ -54,8 +54,8 @@ struct __search_n {
       }
 
       if constexpr (random_access_iterator<_Iter1>) {
-        auto __ret = std::__search_n_random_access_impl<_RangeAlgPolicy>(
-            __first, __last, __count, __value, __pred, __proj, __size);
+        auto __ret =
+            std::__search_n_random_access_impl<_RangeAlgPolicy>(__first, __count, __value, __pred, __proj, __size);
         return {std::move(__ret.first), std::move(__ret.second)};
       }
     }

diff  --git a/libcxx/include/__algorithm/search_n.h b/libcxx/include/__algorithm/search_n.h
index 38474e1b2379d..0962542e134cd 100644
--- a/libcxx/include/__algorithm/search_n.h
+++ b/libcxx/include/__algorithm/search_n.h
@@ -14,11 +14,7 @@
 #include <__algorithm/iterator_operations.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__iterator/advance.h>
-#include <__iterator/concepts.h>
-#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
-#include <__ranges/concepts.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_callable.h>
@@ -68,44 +64,60 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_Iter, _Iter> __search_
   }
 }
 
-template <class _AlgPolicy, class _Pred, class _Iter, class _Sent, class _SizeT, class _Type, class _Proj, class _DiffT>
+// Finds the longest suffix in [__first, __last) where each element satisfies __pred.
+template <class _RAIter, class _Pred, class _Proj, class _ValueT>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _RAIter
+__find_longest_suffix(_RAIter __first, _RAIter __last, const _ValueT& __value, _Pred& __pred, _Proj& __proj) {
+  while (__first != __last) {
+    if (!std::__invoke(__pred, std::__invoke(__proj, *--__last), __value)) {
+      return ++__last;
+    }
+  }
+  return __first;
+}
+
+template <class _AlgPolicy, class _Pred, class _Iter, class _SizeT, class _Type, class _Proj, class _DiffT>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 std::pair<_Iter, _Iter> __search_n_random_access_impl(
-    _Iter __first, _Sent __last, _SizeT __count, const _Type& __value, _Pred& __pred, _Proj& __proj, _DiffT __size1) {
-  using 
diff erence_type = typename iterator_traits<_Iter>::
diff erence_type;
+    _Iter __first, _SizeT __count_in, const _Type& __value, _Pred& __pred, _Proj& __proj, _DiffT __size) {
+  auto __last  = __first + __size;
+  auto __count = static_cast<_DiffT>(__count_in);
+
   if (__count == 0)
     return std::make_pair(__first, __first);
-  if (__size1 < static_cast<_DiffT>(__count)) {
-    _IterOps<_AlgPolicy>::__advance_to(__first, __last);
-    return std::make_pair(__first, __first);
-  }
+  if (__size < __count)
+    return std::make_pair(__last, __last);
+
+  // [__match_start, __match_start + __count) is the subrange which we currently check whether it only contains matching
+  // elements. This subrange is returned in case all the elements match.
+  // [__match_start, __matched_until) is the longest subrange where all elements are known to match at any given point
+  // in time.
+  // [__matched_until, __match_start + __count) is the subrange where we don't know whether the elements match.
+
+  // This algorithm tries to expand the subrange [__match_start, __matched_until) into a range of sufficient length.
+  // When we fail to do that because we find a mismatching element, we move it forward to the beginning of the next
+  // consecutive sequence that is not known not to match.
+
+  const _Iter __try_match_until = __last - __count;
+  _Iter __match_start           = __first;
+  _Iter __matched_until         = __first;
 
-  const auto __s = __first + __size1 - 
diff erence_type(__count - 1); // Start of pattern match can't go beyond here
   while (true) {
-    // Find first element in sequence that matchs __value, with a mininum of loop checks
-    while (true) {
-      if (__first >= __s) { // return __last if no element matches __value
-        _IterOps<_AlgPolicy>::__advance_to(__first, __last);
-        return std::make_pair(__first, __first);
-      }
-      if (std::__invoke(__pred, std::__invoke(__proj, *__first), __value))
-        break;
-      ++__first;
-    }
-    // *__first matches __value_, now match elements after here
-    auto __m = __first;
-    _SizeT __c(0);
-    while (true) {
-      if (++__c == __count) // If pattern exhausted, __first is the answer (works for 1 element pattern)
-        return std::make_pair(__first, __first + _DiffT(__count));
-      ++__m; // no need to check range on __m because __s guarantees we have enough source
+    // There's no chance of expanding the subrange into a sequence of sufficient length, since we don't have enough
+    // elements in the haystack anymore.
+    if (__match_start > __try_match_until)
+      return std::make_pair(__last, __last);
 
-      // if there is a mismatch, restart with a new __first
-      if (!std::__invoke(__pred, std::__invoke(__proj, *__m), __value)) {
-        __first = __m;
-        ++__first;
-        break;
-      } // else there is a match, check next elements
-    }
+    auto __mismatch = std::__find_longest_suffix(__matched_until, __match_start + __count, __value, __pred, __proj);
+
+    // If all elements in [__matched_until, __match_start + __count) match, we know that
+    // [__match_start, __match_start + __count) is a full sequence of matching elements, so we're done.
+    if (__mismatch == __matched_until)
+      return std::make_pair(__match_start, __match_start + __count);
+
+    // Otherwise, we have to move the [__match_start, __matched_until) subrange forward past the point where we know for
+    // sure a match is impossible.
+    __matched_until = __match_start + __count;
+    __match_start   = __mismatch;
   }
 }
 
@@ -119,7 +131,7 @@ template <class _Iter,
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_Iter, _Iter>
 __search_n_impl(_Iter __first, _Sent __last, _DiffT __count, const _Type& __value, _Pred& __pred, _Proj& __proj) {
   return std::__search_n_random_access_impl<_ClassicAlgPolicy>(
-      __first, __last, __count, __value, __pred, __proj, __last - __first);
+      __first, __count, __value, __pred, __proj, __last - __first);
 }
 
 template <class _Iter1,

diff  --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
index de404fedaed3a..91c6ad3eafa0d 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -59,10 +59,56 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
-          ->Arg(1000) // non power-of-two
+          ->Arg(32)
           ->Arg(1024)
-          ->Arg(8192)
-          ->Arg(1 << 20);
+          ->Arg(8192);
+    };
+    // {std,ranges}::search_n
+    bm.operator()<std::vector<int>>("std::search_n(vector<int>) (no match)", std_search_n);
+    bm.operator()<std::deque<int>>("std::search_n(deque<int>) (no match)", std_search_n);
+    bm.operator()<std::list<int>>("std::search_n(list<int>) (no match)", std_search_n);
+    bm.operator()<std::vector<int>>("rng::search_n(vector<int>) (no match)", std::ranges::search_n);
+    bm.operator()<std::deque<int>>("rng::search_n(deque<int>) (no match)", std::ranges::search_n);
+    bm.operator()<std::list<int>>("rng::search_n(list<int>) (no match)", std::ranges::search_n);
+
+    // {std,ranges}::search_n(pred)
+    bm.operator()<std::vector<int>>("std::search_n(vector<int>, pred) (no match)", std_search_n_pred);
+    bm.operator()<std::deque<int>>("std::search_n(deque<int>, pred) (no match)", std_search_n_pred);
+    bm.operator()<std::list<int>>("std::search_n(list<int>, pred) (no match)", std_search_n_pred);
+    bm.operator()<std::vector<int>>("rng::search_n(vector<int>, pred) (no match)", ranges_search_n_pred);
+    bm.operator()<std::deque<int>>("rng::search_n(deque<int>, pred) (no match)", ranges_search_n_pred);
+    bm.operator()<std::list<int>>("rng::search_n(list<int>, pred) (no match)", ranges_search_n_pred);
+  }
+
+  // Benchmark {std,ranges}::search_n where the needle almost matches a lot.
+  {
+    auto bm = []<class Container>(std::string name, auto search_n) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search_n](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_
diff erent_from({x});
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack
+
+            // Make sure there are no actual matches
+            for (size_t i = 0; i < size; i += getRandomInteger<size_t>(1, n - 1)) {
+              *std::next(haystack.begin(), i) = y;
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(n);
+              benchmark::DoNotOptimize(y);
+              auto result = search_n(haystack.begin(), haystack.end(), n, x);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(1024)
+          ->Arg(8192);
     };
     // {std,ranges}::search_n
     bm.operator()<std::vector<int>>("std::search_n(vector<int>) (no match)", std_search_n);

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/ranges.search_n.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/ranges.search_n.pass.cpp
index 2f2e436c79130..f68c31ead7b8f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/ranges.search_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/ranges.search_n.pass.cpp
@@ -186,18 +186,50 @@ constexpr void test_iterators() {
   }
 
   { // check that the first match is returned
-    {
-      int a[] = {6, 6, 8, 6, 6, 8, 6, 6, 8};
-      auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 2, 6);
-      assert(base(ret.begin()) == a);
-      assert(base(ret.end()) == a + 2);
+    { // Match is at the start
+      {
+        int a[]  = {6, 6, 8, 6, 6, 8, 6, 6, 8};
+        auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 2, 6);
+        assert(base(ret.begin()) == a);
+        assert(base(ret.end()) == a + 2);
+      }
+      {
+        int a[]    = {6, 6, 8, 6, 6, 8, 6, 6, 8};
+        auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
+        auto ret   = std::ranges::search_n(range, 2, 6);
+        assert(base(ret.begin()) == a);
+        assert(base(ret.end()) == a + 2);
+      }
     }
-    {
-      int a[] = {6, 6, 8, 6, 6, 8, 6, 6, 8};
-      auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
-      auto ret = std::ranges::search_n(range, 2, 6);
-      assert(base(ret.begin()) == a);
-      assert(base(ret.end()) == a + 2);
+    { // Match is in the middle
+      {
+        int a[]  = {6, 8, 8, 6, 6, 8, 6, 6, 8};
+        auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 2, 6);
+        assert(base(ret.begin()) == a + 3);
+        assert(base(ret.end()) == a + 5);
+      }
+      {
+        int a[]    = {6, 8, 8, 6, 6, 8, 6, 6, 8};
+        auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
+        auto ret   = std::ranges::search_n(range, 2, 6);
+        assert(base(ret.begin()) == a + 3);
+        assert(base(ret.end()) == a + 5);
+      }
+    }
+    { // Match is at the end
+      {
+        int a[]  = {6, 6, 8, 6, 6, 8, 6, 6, 6};
+        auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 3, 6);
+        assert(base(ret.begin()) == a + 6);
+        assert(base(ret.end()) == a + 9);
+      }
+      {
+        int a[]    = {6, 6, 8, 6, 6, 8, 6, 6, 6};
+        auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
+        auto ret   = std::ranges::search_n(range, 3, 6);
+        assert(base(ret.begin()) == a + 6);
+        assert(base(ret.end()) == a + 9);
+      }
     }
   }
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/search_n.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/search_n.pass.cpp
index 228bc6b768cf9..514dc9a911302 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/search_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.search/search_n.pass.cpp
@@ -14,79 +14,83 @@
 //            const T& value);
 
 #include <algorithm>
+#include <array>
 #include <cassert>
 
 #include "test_macros.h"
 #include "test_iterators.h"
-#include "user_defined_integral.h"
-
-#if TEST_STD_VER > 17
-TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {0, 0, 1, 1, 2, 2};
-    return    (std::search_n(std::begin(ia), std::end(ia), 1, 0) == ia)
-           && (std::search_n(std::begin(ia), std::end(ia), 2, 1) == ia+2)
-           && (std::search_n(std::begin(ia), std::end(ia), 1, 3) == std::end(ia))
-           ;
-    }
-#endif
 
 template <class Iter>
-void
-test()
-{
-    int ia[] = {0, 1, 2, 3, 4, 5};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 0, 0) == Iter(ia));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 1, 0) == Iter(ia+0));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 2, 0) == Iter(ia+sa));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), sa, 0) == Iter(ia+sa));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 0, 3) == Iter(ia));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 1, 3) == Iter(ia+3));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 2, 3) == Iter(ia+sa));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), sa, 3) == Iter(ia+sa));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 0, 5) == Iter(ia));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 1, 5) == Iter(ia+5));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), 2, 5) == Iter(ia+sa));
-    assert(std::search_n(Iter(ia), Iter(ia+sa), sa, 5) == Iter(ia+sa));
-
-    int ib[] = {0, 0, 1, 1, 2, 2};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 0, 0) == Iter(ib));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 1, 0) == Iter(ib+0));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 2, 0) == Iter(ib+0));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 3, 0) == Iter(ib+sb));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), sb, 0) == Iter(ib+sb));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 0, 1) == Iter(ib));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 1, 1) == Iter(ib+2));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 2, 1) == Iter(ib+2));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 3, 1) == Iter(ib+sb));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), sb, 1) == Iter(ib+sb));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 0, 2) == Iter(ib));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 1, 2) == Iter(ib+4));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 2, 2) == Iter(ib+4));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), 3, 2) == Iter(ib+sb));
-    assert(std::search_n(Iter(ib), Iter(ib+sb), sb, 2) == Iter(ib+sb));
-
-    int ic[] = {0, 0, 0};
-    const unsigned sc = sizeof(ic)/sizeof(ic[0]);
-    assert(std::search_n(Iter(ic), Iter(ic+sc), 0, 0) == Iter(ic));
-    assert(std::search_n(Iter(ic), Iter(ic+sc), 1, 0) == Iter(ic));
-    assert(std::search_n(Iter(ic), Iter(ic+sc), 2, 0) == Iter(ic));
-    assert(std::search_n(Iter(ic), Iter(ic+sc), 3, 0) == Iter(ic));
-    assert(std::search_n(Iter(ic), Iter(ic+sc), 4, 0) == Iter(ic+sc));
+TEST_CONSTEXPR_CXX20 bool test() {
+  { // simple test
+    int a[]  = {1, 2, 3, 4, 5, 6};
+    auto ret = std::search_n(Iter(a), Iter(a + 6), 1, 3);
+    assert(base(ret) == a + 2);
+  }
+  { // matching part begins at the front
+    int a[]  = {7, 7, 3, 7, 3, 6};
+    auto ret = std::search_n(Iter(a), Iter(a + 6), 2, 7);
+    assert(base(ret) == a);
+  }
+  { // matching part ends at the back
+    int a[]  = {9, 3, 6, 4, 4};
+    auto ret = std::search_n(Iter(a), Iter(a + 5), 2, 4);
+    assert(base(ret) == a + 3);
+  }
+  { // pattern does not match
+    int a[]  = {9, 3, 6, 4, 8};
+    auto ret = std::search_n(Iter(a), Iter(a + 5), 1, 1);
+    assert(base(ret) == a + 5);
+  }
+  { // range and pattern are identical
+    int a[]  = {1, 1, 1, 1};
+    auto ret = std::search_n(Iter(a), Iter(a + 4), 4, 1);
+    assert(base(ret) == a);
+  }
+  { // pattern is longer than range
+    int a[]  = {3, 3, 3};
+    auto ret = std::search_n(Iter(a), Iter(a + 3), 4, 3);
+    assert(base(ret) == a + 3);
+  }
+  { // pattern has zero length
+    int a[]  = {6, 7, 8};
+    auto ret = std::search_n(Iter(a), Iter(a + 3), 0, 7);
+    assert(base(ret) == a);
+  }
+  { // range has zero length
+    std::array<int, 0> a = {};
+    auto ret             = std::search_n(Iter(a.data()), Iter(a.data()), 1, 1);
+    assert(base(ret) == a.data());
+  }
+  {   // 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(Iter(a), Iter(a + 9), 2, 6);
+      assert(base(ret) == a);
+    }
+    { // Match is in the middle
+      int a[]  = {6, 8, 8, 6, 6, 8, 6, 6, 8};
+      auto ret = std::search_n(Iter(a), Iter(a + 9), 2, 6);
+      assert(base(ret) == a + 3);
+    }
+    { // Match is at the end
+      int a[]  = {6, 6, 8, 6, 6, 8, 6, 6, 6};
+      auto ret = std::search_n(Iter(a), Iter(a + 9), 3, 6);
+      assert(base(ret) == a + 6);
+    }
+  }
 
-    // Check that we properly convert the size argument to an integral.
-    (void)std::search_n(Iter(ic), Iter(ic+sc), UserDefinedIntegral<unsigned>(0), 0);
+  return true;
 }
 
-int main(int, char**)
-{
-    test<forward_iterator<const int*> >();
-    test<bidirectional_iterator<const int*> >();
-    test<random_access_iterator<const int*> >();
-
-#if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+int main(int, char**) {
+  test<forward_iterator<const int*> >();
+  test<bidirectional_iterator<const int*> >();
+  test<random_access_iterator<const int*> >();
+#if TEST_STD_VER >= 20
+  static_assert(test<forward_iterator<const int*> >());
+  static_assert(test<bidirectional_iterator<const int*> >());
+  static_assert(test<random_access_iterator<const int*> >());
 #endif
 
   return 0;


        


More information about the libcxx-commits mailing list