libstdc++: Fix ranges::search_n for random access iterators [PR97828]
authorPatrick Palka <ppalka@redhat.com>
Tue, 17 Nov 2020 15:28:20 +0000 (10:28 -0500)
committerPatrick Palka <ppalka@redhat.com>
Tue, 17 Nov 2020 15:28:20 +0000 (10:28 -0500)
My ranges transcription of the std::search_n implementation for random
access iterators missed a crucial part of the algorithm which the
existing tests didn't exercise.  When __remainder is less than __count
at the start of an iteration of the outer while loop, it means we're
continuing a partial match of __count - __remainder elements from the
previous iteration.  If at the end of the iteration we don't complete
this partial match, we need to reset __remainder so that it's only
offset by the size of the most recent partial match before starting the
next iteration.

This patch fixes this appropriately, mirroring how it's done in the
corresponding std::search_n implementation.

libstdc++-v3/ChangeLog:

PR libstdc++/97828
* include/bits/ranges_algo.h (__search_n_fn::operator()): Check
random_access_iterator before using the backtracking
implementation.  When the backwards scan fails prematurely,
reset __remainder appropriately.
* testsuite/25_algorithms/search_n/97828.cc: New test.

libstdc++-v3/include/bits/ranges_algo.h
libstdc++-v3/testsuite/25_algorithms/search_n/97828.cc [new file with mode: 0644]

index f1a4cc24c0d59d9872bda619b362a7cc249192c7..3905fe44fb2dba4bd264ee09b19f5342a701d257 100644 (file)
@@ -579,7 +579,8 @@ namespace ranges
              }
          }
 
-       if constexpr (sized_sentinel_for<_Sent, _Iter>)
+       if constexpr (sized_sentinel_for<_Sent, _Iter>
+                     && random_access_iterator<_Iter>)
          {
            auto __tail_size = __last - __first;
            auto __remainder = __count;
@@ -594,6 +595,7 @@ namespace ranges
                    if (--__remainder == 0)
                      return {__first - __count, __first};
                  }
+               __remainder = __count + 1 - (__first - __backtrack);
              }
            auto __i = __first + __tail_size;
            return {__i, __i};
diff --git a/libstdc++-v3/testsuite/25_algorithms/search_n/97828.cc b/libstdc++-v3/testsuite/25_algorithms/search_n/97828.cc
new file mode 100644 (file)
index 0000000..f692369
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+// PR libstdc++/97828
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_sized_range;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::random_access_iterator_wrapper;
+
+template<template<typename> typename Wrapper>
+void
+test01()
+{
+  int x[] = {0,42,42,0,42,42,42};
+  test_sized_range<int, Wrapper> rx(x);
+  auto res = std::ranges::search_n(rx, 3, 42);
+  VERIFY( res.begin().ptr == x+4 && res.end().ptr == x+7 );
+}
+
+template<template<typename> typename Wrapper>
+void
+test02()
+{
+  int x[] = {0,42,42,0,42};
+  test_sized_range<int, Wrapper> rx(x);
+  auto res = std::ranges::search_n(rx, 3, 42);
+  VERIFY( res.begin().ptr == x+5 && res.end().ptr == x+5 );
+}
+
+int
+main()
+{
+  test01<forward_iterator_wrapper>();
+  test01<random_access_iterator_wrapper>();
+  test02<forward_iterator_wrapper>();
+  test02<random_access_iterator_wrapper>();
+}