[libcxx-commits] [libcxx] [libc++] Optimize ranges::copy for forward_iterator and segmented_iterator (PR #120134)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Sun Mar 23 08:34:17 PDT 2025


https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/120134

>From 2863a813afee1f5be1564a99d043bc43fe307575 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Mon, 16 Dec 2024 13:30:56 -0500
Subject: [PATCH 1/3] Speed-up range operations in vector<bool>

---
 libcxx/include/__algorithm/copy.h             |  79 +++++++++++++
 libcxx/include/__bit_reference                |  11 ++
 libcxx/include/__vector/vector_bool.h         |   1 -
 .../vector_bool_operations.bench.cpp          | 110 ++++++++++++++++++
 .../alg.copy/copy.pass.cpp                    |  33 ++++++
 .../test/std/containers/from_range_helpers.h  |  38 ++++++
 6 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp

diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index ea98031df11ad..955389ad8f166 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -14,11 +14,13 @@
 #include <__algorithm/min.h>
 #include <__config>
 #include <__fwd/bit_reference.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/segmented_iterator.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_convertible.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
 
@@ -154,6 +156,25 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
   return __result;
 }
 
+template <class _InIter,
+          class _Sent,
+          __enable_if_t<__has_input_iterator_category<_InIter>::value &&
+                            !__has_random_access_iterator_category<_InIter>::value,
+                        int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InIter>::difference_type
+__iter_sent_distance(_InIter __first, _Sent __last) {
+  typename iterator_traits<_InIter>::difference_type __r(0);
+  for (; __first != __last; ++__first)
+    ++__r;
+  return __r;
+}
+
+template <class _InIter, class _Sent, __enable_if_t<__has_random_access_iterator_category<_InIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 typename iterator_traits<_InIter>::difference_type
+__iter_sent_distance(_InIter __first, _Sent __last) {
+  return static_cast<typename iterator_traits<_InIter>::difference_type>(__last - __first);
+}
+
 struct __copy_impl {
   template <class _InIter, class _Sent, class _OutIter>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
@@ -227,6 +248,64 @@ struct __copy_impl {
     return std::make_pair(__last, std::__copy_unaligned(__first, __last, __result));
   }
 
+  template <class _InIter,
+            class _Sent,
+            class _Cp,
+            __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
+                           __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+                              is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
+                          int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
+  operator()(_InIter __first, _Sent __last, __bit_iterator<_Cp, false> __result) const {
+    using _It            = __bit_iterator<_Cp, false>;
+    using __storage_type = typename _It::__storage_type;
+#if _LIBCPP_STD_VER >= 20
+    __storage_type __n = static_cast<__storage_type>(std::ranges::distance(__first, __last));
+#else
+    __storage_type __n = static_cast<__storage_type>(std::__iter_sent_distance(__first, __last));
+#endif
+    const unsigned __bits_per_word = _It::__bits_per_word;
+
+    if (__first != __last) {
+      // do first partial word, if present
+      if (__result.__ctz_ != 0) {
+        __storage_type __clz = static_cast<__storage_type>(__bits_per_word - __result.__ctz_);
+        __storage_type __dn  = std::min(__clz, __n);
+        __storage_type __w   = *__result.__seg_;
+        __storage_type __m   = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz - __dn));
+        __w &= ~__m;
+        for (__storage_type __i = 0; __i < __dn; ++__i, ++__first)
+          __w |= static_cast<__storage_type>(*__first) << __result.__ctz_++;
+        *__result.__seg_ = __w;
+        if (__result.__ctz_ == __bits_per_word) {
+          __result.__ctz_ = 0;
+          ++__result.__seg_;
+        }
+        __n -= __dn;
+      }
+    }
+    // do middle whole words, if present
+    __storage_type __nw = __n / __bits_per_word;
+    __n -= __nw * __bits_per_word;
+    for (; __nw; --__nw) {
+      __storage_type __w = 0;
+      for (__storage_type __i = 0; __i < __bits_per_word; ++__i, ++__first)
+        __w |= static_cast<__storage_type>(*__first) << __i;
+      *__result.__seg_++ = __w;
+    }
+    // do last partial word, if present
+    if (__n) {
+      __storage_type __w = 0;
+      for (__storage_type __i = 0; __i < __n; ++__i, ++__first)
+        __w |= static_cast<__storage_type>(*__first) << __i;
+      __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
+      *__result.__seg_ &= ~__m;
+      *__result.__seg_ |= __w;
+      __result.__ctz_ = __n;
+    }
+    return std::make_pair(std::move(__first), std::move(__result));
+  }
+
   // At this point, the iterators have been unwrapped so any `contiguous_iterator` has been unwrapped to a pointer.
   template <class _In, class _Out, __enable_if_t<__can_lower_copy_assignment_to_memmove<_In, _Out>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_In*, _Out*>
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index f5c22fc0a3ade..3d8c333a7614f 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -35,6 +35,7 @@
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_unsigned.h>
+#include <__type_traits/is_convertible.h>
 #include <__type_traits/void_t.h>
 #include <__utility/pair.h>
 #include <__utility/swap.h>
@@ -463,6 +464,16 @@ private:
   template <class _Dp>
   friend struct __bit_array;
 
+  template <class _InIter,
+            class _Sent,
+            class _Dp,
+            __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
+                           __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+                              is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
+                          int> >
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 friend pair<_InIter, __bit_iterator<_Dp, false> >
+  __copy_impl::operator()(_InIter __first, _Sent __last, __bit_iterator<_Dp, false> __result) const;
+
   template <bool _FillVal, class _Dp>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 friend void
   __fill_n_bool(__bit_iterator<_Dp, false> __first, typename __size_difference_type_traits<_Dp>::size_type __n);
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 569cc5ea898bc..32d818038dfe2 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -174,7 +174,6 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
     if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
       auto __n = static_cast<size_type>(ranges::distance(__range));
       __init_with_size(ranges::begin(__range), ranges::end(__range), __n);
-
     } else {
       __init_with_sentinel(ranges::begin(__range), ranges::end(__range));
     }
diff --git a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
new file mode 100644
index 0000000000000..a434bdbbc6a3e
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "ContainerBenchmarks.h"
+#include "../../std/containers/from_range_helpers.h"
+#include "../GenerateInput.h"
+#include "test_iterators.h"
+
+using namespace ContainerBenchmarks;
+
+using vb_iter = std::vector<bool>::iterator;
+
+// Benchmarks for forward_iterator or forward_range
+
+BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
+                  forward_iterator,
+                  getRandomIntegerInputs<bool>,
+                  forward_iterator<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
+                  forward_range,
+                  getRandomIntegerInputs<bool>,
+                  forward_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+    BM_AssignIterIter<std::vector<bool>>, forward_iterator, getRandomIntegerInputs<bool>, forward_iterator<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+    BM_AssignRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
+                  forward_iterator,
+                  getRandomIntegerInputs<bool>,
+                  forward_iterator<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+    BM_InsertRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(
+    BM_AppendRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+// Benchmarks for random_access_iterator or random_access_range
+
+BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
+                  random_access_iterator,
+                  getRandomIntegerInputs<bool>,
+                  random_access_iterator<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
+                  random_access_range,
+                  getRandomIntegerInputs<bool>,
+                  random_access_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_AssignIterIter<std::vector<bool>>,
+                  random_access_iterator,
+                  getRandomIntegerInputs<bool>,
+                  random_access_iterator<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_AssignRange<std::vector<bool>>,
+                  random_access_range,
+                  getRandomIntegerInputs<bool>,
+                  random_access_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
+                  random_access_iterator,
+                  getRandomIntegerInputs<bool>,
+                  random_access_iterator<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_InsertRange<std::vector<bool>>,
+                  random_access_range,
+                  getRandomIntegerInputs<bool>,
+                  random_access_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_CAPTURE(BM_AppendRange<std::vector<bool>>,
+                  random_access_range,
+                  getRandomIntegerInputs<bool>,
+                  random_access_range_wrapper<vb_iter>())
+    ->Arg(5140480);
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
index cfcaf1c8a6dd7..21f1e3b062365 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
@@ -15,6 +15,7 @@
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 #include <algorithm>
+#include <array>
 #include <cassert>
 #include <vector>
 
@@ -64,6 +65,30 @@ struct TestInIters {
   }
 };
 
+template <std::size_t N>
+struct TestFwdIterInBitIterOut {
+  std::array<bool, N> in = {};
+  template <class FwdIter>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+    for (std::size_t i = 0; i < in.size(); i += 2)
+      in[i] = true;
+
+    { // Test with full bytes
+      std::vector<bool> out(N);
+      std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin());
+      for (std::size_t i = 0; i < N; ++i)
+        assert(out[i] == static_cast<bool>(in[i]));
+    }
+    { // Test with partial bytes in both front and back
+      std::vector<bool> out(N + 8);
+      std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin() + 4);
+      for (std::size_t i = 0; i < N; ++i)
+        assert(out[i + 4] == static_cast<bool>(in[i]));
+    }
+  }
+};
+
+
 TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
   std::vector<bool> in(N, false);
   for (std::size_t i = 0; i < N; i += 2)
@@ -259,6 +284,14 @@ TEST_CONSTEXPR_CXX20 bool test() {
     }
   }
 
+  { // Test std::copy() with forward_iterator-pair inputs and vector<bool>::iterator output
+    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<8>());
+    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<19>());
+    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<32>());
+    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<64>());
+    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<299>());
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index edf1d6ce528d7..3b703dbe4d9b7 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -50,6 +50,44 @@ constexpr auto wrap_input(std::vector<T>& input) {
   return std::ranges::subrange(std::move(b), std::move(e));
 }
 
+template <class Iter, class Sent = Iter>
+class forward_range_wrapper {
+public:
+  using _Iter = forward_iterator<Iter>;
+  using _Sent = sentinel_wrapper<_Iter>;
+
+  forward_range_wrapper() = default;
+  forward_range_wrapper(Iter begin, Iter end) : begin_(std::move(begin)), end_(std::move(end)) {}
+  _Iter begin() { return _Iter(std::move(begin_)); }
+  _Sent end() { return _Sent(_Iter(std::move(end_))); }
+
+private:
+  Iter begin_;
+  Sent end_;
+};
+
+template <class Iter, class Sent>
+forward_range_wrapper(Iter, Sent) -> forward_range_wrapper<Iter, Sent>;
+
+template <class Iter, class Sent = Iter>
+class random_access_range_wrapper {
+public:
+  using _Iter = cpp20_random_access_iterator<Iter>;
+  using _Sent = sized_sentinel<_Iter>;
+
+  random_access_range_wrapper() = default;
+  random_access_range_wrapper(Iter begin, Iter end) : begin_(std::move(begin)), end_(std::move(end)) {}
+  _Iter begin() { return _Iter(std::move(begin_)); }
+  _Sent end() { return _Sent(_Iter(std::move(end_))); }
+
+private:
+  Iter begin_;
+  Sent end_;
+};
+
+template <class Iter, class Sent>
+random_access_range_wrapper(Iter, Sent) -> random_access_range_wrapper<Iter, Sent>;
+
 struct KeyValue {
   int key;    // Only the key is considered for equality comparison.
   char value; // Allows distinguishing equivalent instances.

>From 1924d9eecbcdd23cf3f2f1ad4a9d79a430f50bd8 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sat, 22 Mar 2025 10:53:54 -0400
Subject: [PATCH 2/3] Fix mask and add copy benchmark

---
 libcxx/include/__algorithm/copy.h             | 24 +++++++---
 libcxx/include/__bit_reference                |  7 +--
 .../algorithms/modifying/copy.bench.cpp       | 45 +++++++++++++++++--
 .../alg.copy/copy.pass.cpp                    | 12 ++---
 4 files changed, 69 insertions(+), 19 deletions(-)

diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index 955389ad8f166..2146f0952e1fe 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -242,21 +242,33 @@ struct __copy_impl {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<__bit_iterator<_Cp, _IsConst>, __bit_iterator<_Cp, false> >
   operator()(__bit_iterator<_Cp, _IsConst> __first,
              __bit_iterator<_Cp, _IsConst> __last,
-             __bit_iterator<_Cp, false> __result) const {
+             __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
     if (__first.__ctz_ == __result.__ctz_)
       return std::make_pair(__last, std::__copy_aligned(__first, __last, __result));
     return std::make_pair(__last, std::__copy_unaligned(__first, __last, __result));
   }
 
+  template <class _InIter,
+            class _OutIter,
+            class _Cp,
+            bool _IsConst,
+            __enable_if_t<__is_segmented_iterator<_InIter>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
+  operator()(_InIter __first, _InIter __last, __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
+    std::__for_each_segment(__first, __last, _CopySegment<_InIter, __bit_iterator<_Cp, false> >(__result));
+    return std::make_pair(__last, std::move(__result));
+  }
+
   template <class _InIter,
             class _Sent,
             class _Cp,
-            __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
-                           __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+            __enable_if_t<!__is_segmented_iterator<_InIter>::value &&
+                              (__has_forward_iterator_category<_InIter>::value ||
+                               __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
                               is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
                           int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
-  operator()(_InIter __first, _Sent __last, __bit_iterator<_Cp, false> __result) const {
+  operator()(_InIter __first, _Sent __last, __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
     using _It            = __bit_iterator<_Cp, false>;
     using __storage_type = typename _It::__storage_type;
 #if _LIBCPP_STD_VER >= 20
@@ -272,7 +284,7 @@ struct __copy_impl {
         __storage_type __clz = static_cast<__storage_type>(__bits_per_word - __result.__ctz_);
         __storage_type __dn  = std::min(__clz, __n);
         __storage_type __w   = *__result.__seg_;
-        __storage_type __m   = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz - __dn));
+        __storage_type __m   = std::__middle_mask<__storage_type>(__clz - __dn, __result.__ctz_);
         __w &= ~__m;
         for (__storage_type __i = 0; __i < __dn; ++__i, ++__first)
           __w |= static_cast<__storage_type>(*__first) << __result.__ctz_++;
@@ -298,7 +310,7 @@ struct __copy_impl {
       __storage_type __w = 0;
       for (__storage_type __i = 0; __i < __n; ++__i, ++__first)
         __w |= static_cast<__storage_type>(*__first) << __i;
-      __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
+      __storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
       *__result.__seg_ &= ~__m;
       *__result.__seg_ |= __w;
       __result.__ctz_ = __n;
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 3d8c333a7614f..19fae037803e5 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -33,9 +33,9 @@
 #include <__type_traits/desugars_to.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_convertible.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_unsigned.h>
-#include <__type_traits/is_convertible.h>
 #include <__type_traits/void_t.h>
 #include <__utility/pair.h>
 #include <__utility/swap.h>
@@ -467,8 +467,9 @@ private:
   template <class _InIter,
             class _Sent,
             class _Dp,
-            __enable_if_t<(__has_forward_iterator_category<_InIter>::value ||
-                           __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
+            __enable_if_t<!__is_segmented_iterator<_InIter>::value &&
+                              (__has_forward_iterator_category<_InIter>::value ||
+                               __has_iterator_concept_convertible_to<_InIter, forward_iterator_tag>::value) &&
                               is_convertible<typename iterator_traits<_InIter>::value_type, bool>::value,
                           int> >
   _LIBCPP_CONSTEXPR_SINCE_CXX14 friend pair<_InIter, __bit_iterator<_Dp, false> >
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
index 3549d918478bd..c6ace90965789 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -18,6 +18,7 @@
 
 #include "benchmark/benchmark.h"
 #include "../../GenerateInput.h"
+#include "test_iterators.h"
 #include "test_macros.h"
 
 int main(int argc, char** argv) {
@@ -25,14 +26,15 @@ int main(int argc, char** argv) {
 
   // {std,ranges}::copy(normal container)
   {
-    auto bm = []<class Container>(std::string name, auto copy) {
+    auto bm = []<class ContainerIn, class ContainerOut = std::vector<typename ContainerIn::value_type>>(
+                  std::string name, auto copy) {
       benchmark::RegisterBenchmark(name, [copy](auto& st) {
         std::size_t const n = st.range(0);
-        using ValueType     = typename Container::value_type;
-        Container c;
+        using ValueType     = typename ContainerIn::value_type;
+        ContainerIn c;
         std::generate_n(std::back_inserter(c), n, [] { return Generate<ValueType>::random(); });
 
-        std::vector<ValueType> out(n);
+        ContainerOut out(n);
 
         for ([[maybe_unused]] auto _ : st) {
           benchmark::DoNotOptimize(c);
@@ -42,12 +44,21 @@ int main(int argc, char** argv) {
         }
       })->Range(8, 1 << 20);
     };
+    // Copy from normal containers to vector<int>
     bm.operator()<std::vector<int>>("std::copy(vector<int>)", std_copy);
     bm.operator()<std::deque<int>>("std::copy(deque<int>)", std_copy);
     bm.operator()<std::list<int>>("std::copy(list<int>)", std_copy);
     bm.operator()<std::vector<int>>("rng::copy(vector<int>)", std::ranges::copy);
     bm.operator()<std::deque<int>>("rng::copy(deque<int>)", std::ranges::copy);
     bm.operator()<std::list<int>>("rng::copy(list<int>)", std::ranges::copy);
+
+    // Copy from normal containers to vector<bool>
+    bm.operator()<std::vector<int>, std::vector<bool>>("std::copy(vector<int>, std::vector<bool>)", std_copy);
+    bm.operator()<std::deque<int>, std::vector<bool>>("std::copy(deque<int>, std::vector<bool>)", std_copy);
+    bm.operator()<std::list<int>, std::vector<bool>>("std::copy(list<int>, std::vector<bool>)", std_copy);
+    bm.operator()<std::vector<int>, std::vector<bool>>("rng::copy(vector<int>, std::vector<bool>)", std::ranges::copy);
+    bm.operator()<std::deque<int>, std::vector<bool>>("rng::copy(deque<int>, std::vector<bool>)", std::ranges::copy);
+    bm.operator()<std::list<int>, std::vector<bool>>("rng::copy(list<int>, std::vector<bool>)", std::ranges::copy);
   }
 
   // {std,ranges}::copy(vector<bool>)
@@ -76,6 +87,32 @@ int main(int argc, char** argv) {
 #endif
   }
 
+  // {std,ranges}::copy(forward_iterator, forward_iterator, vector<bool>)
+  {
+    auto bm = []<template <class> class Iter>(std::string name, auto copy) {
+      benchmark::RegisterBenchmark(name, [copy](auto& st) {
+        std::size_t const n = st.range(0);
+        std::vector<int> in(n, 1);
+        std::vector<bool> out(n);
+        auto first = Iter(in.begin());
+        auto last  = Iter(in.end());
+        auto dst   = out.begin();
+        for ([[maybe_unused]] auto _ : st) {
+          benchmark::DoNotOptimize(in);
+          benchmark::DoNotOptimize(out);
+          auto result = copy(first, last, dst);
+          benchmark::DoNotOptimize(result);
+        }
+      })->Range(64, 1 << 20);
+    };
+    bm.operator()<forward_iterator>("std::copy(forward_iterator, vector<bool>)", std_copy);
+    bm.operator()<random_access_iterator>("std::copy(random_access_iterator, vector<bool>)", std_copy);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
+    bm.operator()<forward_iterator>("rng::copy(forward_iterator, vector<bool>)", std::ranges::copy);
+    bm.operator()<random_access_iterator>("rng::copy(random_access_iterator, vector<bool>)", std::ranges::copy);
+#endif
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
index 21f1e3b062365..10909b36b0b4a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
@@ -66,7 +66,7 @@ struct TestInIters {
 };
 
 template <std::size_t N>
-struct TestFwdIterInBitIterOut {
+struct CopyFromForwardIterToBitIter {
   std::array<bool, N> in = {};
   template <class FwdIter>
   TEST_CONSTEXPR_CXX20 void operator()() {
@@ -285,11 +285,11 @@ TEST_CONSTEXPR_CXX20 bool test() {
   }
 
   { // Test std::copy() with forward_iterator-pair inputs and vector<bool>::iterator output
-    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<8>());
-    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<19>());
-    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<32>());
-    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<64>());
-    types::for_each(types::forward_iterator_list<bool*>(), TestFwdIterInBitIterOut<299>());
+    types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<8>());
+    types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<19>());
+    types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<32>());
+    types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<64>());
+    types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<299>());
   }
 
   return true;

>From d6119cd5e28ad3db049797c7b91812f995daba24 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sun, 23 Mar 2025 10:52:23 -0400
Subject: [PATCH 3/3] Add vector<bool> benchmark and cover more segmented
 iterators

---
 libcxx/include/__algorithm/copy.h             |   6 +-
 libcxx/include/__bit_reference                |   1 +
 .../algorithms/modifying/copy.bench.cpp       |   2 +
 .../sequence/sequence_container_benchmarks.h  | 191 +++++++++++++++++-
 .../containers/sequence/vector.bench.cpp      |   1 +
 .../vector_bool_operations.bench.cpp          | 110 ----------
 .../alg.copy/copy.pass.cpp                    | 128 +++++++++---
 .../test/std/containers/from_range_helpers.h  |   4 +-
 8 files changed, 293 insertions(+), 150 deletions(-)
 delete mode 100644 libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp

diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index 2146f0952e1fe..bee3b5d9ff0ea 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -248,11 +248,7 @@ struct __copy_impl {
     return std::make_pair(__last, std::__copy_unaligned(__first, __last, __result));
   }
 
-  template <class _InIter,
-            class _OutIter,
-            class _Cp,
-            bool _IsConst,
-            __enable_if_t<__is_segmented_iterator<_InIter>::value, int> = 0>
+  template <class _InIter, class _Cp, __enable_if_t<__is_segmented_iterator<_InIter>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, __bit_iterator<_Cp, false> >
   operator()(_InIter __first, _InIter __last, __bit_iterator<_Cp, /* IsConst = */ false> __result) const {
     std::__for_each_segment(__first, __last, _CopySegment<_InIter, __bit_iterator<_Cp, false> >(__result));
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 19fae037803e5..ba4b53082ccf7 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -27,6 +27,7 @@
 #include <__functional/identity.h>
 #include <__fwd/bit_reference.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/segmented_iterator.h>
 #include <__memory/construct_at.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
index c6ace90965789..6a175465a1147 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -52,6 +52,7 @@ int main(int argc, char** argv) {
     bm.operator()<std::deque<int>>("rng::copy(deque<int>)", std::ranges::copy);
     bm.operator()<std::list<int>>("rng::copy(list<int>)", std::ranges::copy);
 
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
     // Copy from normal containers to vector<bool>
     bm.operator()<std::vector<int>, std::vector<bool>>("std::copy(vector<int>, std::vector<bool>)", std_copy);
     bm.operator()<std::deque<int>, std::vector<bool>>("std::copy(deque<int>, std::vector<bool>)", std_copy);
@@ -59,6 +60,7 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>, std::vector<bool>>("rng::copy(vector<int>, std::vector<bool>)", std::ranges::copy);
     bm.operator()<std::deque<int>, std::vector<bool>>("rng::copy(deque<int>, std::vector<bool>)", std::ranges::copy);
     bm.operator()<std::list<int>, std::vector<bool>>("rng::copy(list<int>, std::vector<bool>)", std::ranges::copy);
+#endif
   }
 
   // {std,ranges}::copy(vector<bool>)
diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index dcd251d6997dd..db9c45028010f 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -22,6 +22,7 @@
 #include "benchmark/benchmark.h"
 #include "test_iterators.h"
 #include "../../GenerateInput.h"
+#include "../../../std/containers/from_range_helpers.h"
 
 namespace support {
 
@@ -172,8 +173,8 @@ void sequence_container_benchmarks(std::string container) {
       bool toggle = false;
       for ([[maybe_unused]] auto _ : st) {
         std::vector<ValueType>& in = toggle ? in1 : in2;
-        auto first                 = in.data();
-        auto last                  = in.data() + in.size();
+        auto first                 = in.begin();
+        auto last                  = in.end();
         c.assign(cpp17_input_iterator(first), cpp17_input_iterator(last));
         toggle = !toggle;
         DoNotOptimizeData(c);
@@ -237,8 +238,8 @@ void sequence_container_benchmarks(std::string container) {
         std::vector<ValueType> in;
         std::generate_n(std::back_inserter(in), size, gen);
         DoNotOptimizeData(in);
-        auto first = in.data();
-        auto last  = in.data() + in.size();
+        auto first = in.begin();
+        auto last  = in.end();
 
         const int small = 100; // arbitrary
         Container c;
@@ -264,8 +265,8 @@ void sequence_container_benchmarks(std::string container) {
         std::vector<ValueType> in;
         std::generate_n(std::back_inserter(in), size, gen);
         DoNotOptimizeData(in);
-        auto first = in.data();
-        auto last  = in.data() + in.size();
+        auto first = in.begin();
+        auto last  = in.end();
 
         const int overflow = size / 10; // 10% of elements won't fit in the vector when we insert
         Container c;
@@ -290,8 +291,8 @@ void sequence_container_benchmarks(std::string container) {
         std::vector<ValueType> in;
         std::generate_n(std::back_inserter(in), size, gen);
         DoNotOptimizeData(in);
-        auto first = in.data();
-        auto last  = in.data() + in.size();
+        auto first = in.begin();
+        auto last  = in.end();
 
         auto const overflow = 9 * (size / 10); // 90% of elements won't fit in the vector when we insert
         Container c;
@@ -448,6 +449,180 @@ void sequence_container_benchmarks(std::string container) {
         }
       });
   }
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  // Additional benchmarks for vector<bool> iterator-pair and range-based operations
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static constexpr bool is_vector_bool = requires {
+    typename Container::allocator_type;
+  } && std::same_as<std::remove_cvref_t<Container>, std::vector<bool, typename Container::allocator_type>>;
+
+  if constexpr (is_vector_bool) {
+    auto bench_vb = [&](std::string operation, auto f) {
+      benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(1024)->Arg(1 << 16)->Arg(1 << 20);
+    };
+
+    { // iterator-pair ctor
+      auto bm = [&generators, &bench_vb, &tostr]<template <class> class Iter>(std::string iter) {
+        for (auto gen : generators)
+          bench_vb("ctor(" + iter + ", " + iter + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in;
+            std::generate_n(std::back_inserter(in), size, gen);
+            benchmark::DoNotOptimize(in);
+            const auto begin = Iter(in.begin());
+            const auto end   = Iter(in.end());
+            benchmark::DoNotOptimize(in);
+
+            for ([[maybe_unused]] auto _ : st) {
+              Container c(begin, end); // we assume the destructor doesn't dominate the benchmark
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_iterator>("fwd_iter");
+      bm.template operator()<random_access_iterator>("ra_iter");
+    }
+    { // iterator-pair assignment
+      auto bm = [&generators, &bench_vb, &tostr]<template <class> class Iter>(std::string iter) {
+        for (auto gen : generators)
+          bench_vb("assign(" + iter + ", " + iter + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in1, in2;
+            std::generate_n(std::back_inserter(in1), size, gen);
+            std::generate_n(std::back_inserter(in2), size, gen);
+            DoNotOptimizeData(in1);
+            DoNotOptimizeData(in2);
+
+            Container c(in1.begin(), in1.end());
+            bool toggle = true;
+            for ([[maybe_unused]] auto _ : st) {
+              auto& in = toggle ? in2 : in1;
+              c.assign(Iter(in.begin()), Iter(in.end()));
+              toggle = !toggle;
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_iterator>("fwd_iter");
+      bm.template operator()<random_access_iterator>("ra_iter");
+    }
+    { // Iterator-pair insertion
+      auto bm = [&generators, &bench_vb, &tostr]<template <class> class Iter>(std::string iter) {
+        for (auto gen : generators)
+          bench_vb("insert(begin, " + iter + ", " + iter + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in;
+            Container c;
+            std::generate_n(std::back_inserter(in), size, gen);
+            std::generate_n(std::back_inserter(c), size, gen);
+            DoNotOptimizeData(in);
+            DoNotOptimizeData(c);
+
+            for ([[maybe_unused]] auto _ : st) {
+              c.insert(c.begin(), Iter(in.begin()), Iter(in.end()));
+              c.erase(c.begin() + size, c.end()); // avoid growing indefinitely
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_iterator>("fwd_iter");
+      bm.template operator()<random_access_iterator>("ra_iter");
+    }
+
+#if defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L
+    { // Range-ctor
+      auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+        for (auto gen : generators)
+          bench_vb("ctor(" + range + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in;
+            std::generate_n(std::back_inserter(in), size, gen);
+            Range rg(std::ranges::begin(in), std::ranges::end(in));
+            benchmark::DoNotOptimize(in);
+
+            for ([[maybe_unused]] auto _ : st) {
+              Container c(std::from_range, rg); // we assume the destructor doesn't dominate the benchmark
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_range_wrapper>("fwd_range");
+      bm.template operator()<random_access_range_wrapper>("ra_range");
+    }
+    { // Range-assignment
+      auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+        for (auto gen : generators)
+          bench_vb("assign_range(" + range + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in1, in2;
+            std::generate_n(std::back_inserter(in1), size, gen);
+            std::generate_n(std::back_inserter(in2), size, gen);
+            Range rg1(std::ranges::begin(in1), std::ranges::end(in1));
+            Range rg2(std::ranges::begin(in2), std::ranges::end(in2));
+            DoNotOptimizeData(in1);
+            DoNotOptimizeData(in2);
+
+            Container c(std::from_range, rg1);
+            bool toggle = true;
+            for ([[maybe_unused]] auto _ : st) {
+              auto& in = toggle ? rg2 : rg1;
+              c.assign_range(in);
+              toggle = !toggle;
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_range_wrapper>("fwd_range");
+      bm.template operator()<random_access_range_wrapper>("ra_range");
+    }
+    { // Range-insertion
+      auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+        for (auto gen : generators)
+          bench_vb("insert_range(" + range + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in;
+            Container c;
+            std::generate_n(std::back_inserter(in), size, gen);
+            std::generate_n(std::back_inserter(c), size, gen);
+            Range rg(std::ranges::begin(in), std::ranges::end(in));
+            DoNotOptimizeData(in);
+            DoNotOptimizeData(c);
+
+            for ([[maybe_unused]] auto _ : st) {
+              c.insert_range(c.begin(), in);
+              c.erase(c.begin() + size, c.end()); // avoid growing indefinitely
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_range_wrapper>("fwd_range");
+      bm.template operator()<random_access_range_wrapper>("ra_range");
+    }
+    { // Range-append
+      auto bm = [&generators, &bench_vb, &tostr]<template <class, class> class Range>(std::string range) {
+        for (auto gen : generators)
+          bench_vb("append_range(" + range + ")" + tostr(gen), [gen](auto& st) {
+            auto const size = st.range(0);
+            std::vector<int> in;
+            std::generate_n(std::back_inserter(in), size, gen);
+            Range rg(std::ranges::begin(in), std::ranges::end(in));
+            DoNotOptimizeData(in);
+
+            Container c;
+            for ([[maybe_unused]] auto _ : st) {
+              c.append_range(in);
+              c.erase(c.begin(), c.end()); // avoid growing indefinitely
+              DoNotOptimizeData(c);
+            }
+          });
+      };
+      bm.template operator()<forward_range_wrapper>("fwd_range");
+      bm.template operator()<random_access_range_wrapper>("ra_range");
+    }
+#endif
+  }
 }
 
 } // namespace support
diff --git a/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp b/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
index 599db1d90fa9a..fb0b3272689ea 100644
--- a/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
+++ b/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
@@ -17,6 +17,7 @@
 int main(int argc, char** argv) {
   support::sequence_container_benchmarks<std::vector<int>>("std::vector<int>");
   support::sequence_container_benchmarks<std::vector<std::string>>("std::vector<std::string>");
+  support::sequence_container_benchmarks<std::vector<bool>>("std::vector<bool>");
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
deleted file mode 100644
index a434bdbbc6a3e..0000000000000
--- a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <deque>
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-#include "ContainerBenchmarks.h"
-#include "../../std/containers/from_range_helpers.h"
-#include "../GenerateInput.h"
-#include "test_iterators.h"
-
-using namespace ContainerBenchmarks;
-
-using vb_iter = std::vector<bool>::iterator;
-
-// Benchmarks for forward_iterator or forward_range
-
-BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
-                  forward_iterator,
-                  getRandomIntegerInputs<bool>,
-                  forward_iterator<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
-                  forward_range,
-                  getRandomIntegerInputs<bool>,
-                  forward_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
-    BM_AssignIterIter<std::vector<bool>>, forward_iterator, getRandomIntegerInputs<bool>, forward_iterator<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
-    BM_AssignRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
-                  forward_iterator,
-                  getRandomIntegerInputs<bool>,
-                  forward_iterator<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
-    BM_InsertRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(
-    BM_AppendRange<std::vector<bool>>, forward_range, getRandomIntegerInputs<bool>, forward_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-// Benchmarks for random_access_iterator or random_access_range
-
-BENCHMARK_CAPTURE(BM_ConstructIterIter<std::vector<bool>>,
-                  random_access_iterator,
-                  getRandomIntegerInputs<bool>,
-                  random_access_iterator<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_ConstructFromRange<std::vector<bool>>,
-                  random_access_range,
-                  getRandomIntegerInputs<bool>,
-                  random_access_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_AssignIterIter<std::vector<bool>>,
-                  random_access_iterator,
-                  getRandomIntegerInputs<bool>,
-                  random_access_iterator<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_AssignRange<std::vector<bool>>,
-                  random_access_range,
-                  getRandomIntegerInputs<bool>,
-                  random_access_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_InsertIterIterIter<std::vector<bool>>,
-                  random_access_iterator,
-                  getRandomIntegerInputs<bool>,
-                  random_access_iterator<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_InsertRange<std::vector<bool>>,
-                  random_access_range,
-                  getRandomIntegerInputs<bool>,
-                  random_access_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_CAPTURE(BM_AppendRange<std::vector<bool>>,
-                  random_access_range,
-                  getRandomIntegerInputs<bool>,
-                  random_access_range_wrapper<vb_iter>())
-    ->Arg(5140480);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
index 10909b36b0b4a..5afd20e101511 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
@@ -17,6 +17,8 @@
 #include <algorithm>
 #include <array>
 #include <cassert>
+#include <deque>
+#include <ranges>
 #include <vector>
 
 #include "sized_allocator.h"
@@ -65,30 +67,6 @@ struct TestInIters {
   }
 };
 
-template <std::size_t N>
-struct CopyFromForwardIterToBitIter {
-  std::array<bool, N> in = {};
-  template <class FwdIter>
-  TEST_CONSTEXPR_CXX20 void operator()() {
-    for (std::size_t i = 0; i < in.size(); i += 2)
-      in[i] = true;
-
-    { // Test with full bytes
-      std::vector<bool> out(N);
-      std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin());
-      for (std::size_t i = 0; i < N; ++i)
-        assert(out[i] == static_cast<bool>(in[i]));
-    }
-    { // Test with partial bytes in both front and back
-      std::vector<bool> out(N + 8);
-      std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin() + 4);
-      for (std::size_t i = 0; i < N; ++i)
-        assert(out[i + 4] == static_cast<bool>(in[i]));
-    }
-  }
-};
-
-
 TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
   std::vector<bool> in(N, false);
   for (std::size_t i = 0; i < N; i += 2)
@@ -109,6 +87,30 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
   return true;
 }
 
+template <std::size_t N>
+struct CopyFromForwardIterToBitIter {
+  std::array<bool, N> in;
+
+  template <class FwdIter>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+    for (std::size_t i = 0; i < in.size(); i += 2)
+      in[i] = true;
+
+    { // Aligned
+      std::vector<bool> out(N);
+      std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin());
+      for (std::size_t i = 0; i < N; ++i)
+        assert(out[i] == static_cast<bool>(in[i]));
+    }
+    { // Unaligned
+      std::vector<bool> out(N + 8);
+      std::copy(FwdIter(in.data()), FwdIter(in.data() + N), out.begin() + 4);
+      for (std::size_t i = 0; i < N; ++i)
+        assert(out[i + 4] == static_cast<bool>(in[i]));
+    }
+  }
+};
+
 TEST_CONSTEXPR_CXX20 bool test() {
   types::for_each(types::cpp17_input_iterator_list<const int*>(), TestInIters());
 
@@ -284,7 +286,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     }
   }
 
-  { // Test std::copy() with forward_iterator-pair inputs and vector<bool>::iterator output
+  { // Test std::copy when copying from forward_iterators (and above) to vector<bool> iterator
     types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<8>());
     types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<19>());
     types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<32>());
@@ -292,6 +294,82 @@ TEST_CONSTEXPR_CXX20 bool test() {
     types::for_each(types::forward_iterator_list<bool*>(), CopyFromForwardIterToBitIter<299>());
   }
 
+  // Test std::copy with segmented iterators: deque<T>::iterator, join_view::iterator
+  {
+    // std::deque iterator
+    if (!TEST_IS_CONSTANT_EVALUATED) { // TODO: enable this for constant evaluation when std::deque is fully constexpr
+      {                                // Copy from segmented input to contiguous output (deque<int> to vector<int>)
+        std::deque<int> in(20);
+        for (std::size_t i = 0; i < in.size(); ++i)
+          in[i] = i;
+        std::vector<int> out(in.size());
+        std::copy(in.begin(), in.end(), out.begin());
+        assert(std::equal(in.begin(), in.end(), out.begin()));
+      }
+      { // Copy from contiguous input to segmented output (vector<int> to deque<int>)
+        std::vector<int> in(20);
+        for (std::size_t i = 0; i < in.size(); ++i)
+          in[i] = i;
+        std::deque<int> out(in.size());
+        std::copy(in.begin(), in.end(), out.begin());
+        assert(std::equal(in.begin(), in.end(), out.begin()));
+      }
+      { // Copy from segmented input to segmented output (deque<int> to deque<int>)
+        std::deque<int> in(20);
+        for (std::size_t i = 0; i < in.size(); ++i)
+          in[i] = i;
+        std::deque<int> out(in.size());
+        std::copy(in.begin(), in.end(), out.begin());
+        assert(in == out);
+      }
+      { // Copy from segmented input to vector<bool> output
+        std::deque<bool> in(199, false);
+        for (std::size_t i = 0; i < in.size(); i += 2)
+          in[i] = true;
+        std::vector<bool> out(in.size());
+        std::copy(in.begin(), in.end(), out.begin());
+        assert(std::equal(in.begin(), in.end(), out.begin()));
+      }
+      { // Copy from vector<bool> input to segmented output
+        std::vector<bool> in(199, false);
+        for (std::size_t i = 0; i < in.size(); i += 2)
+          in[i] = true;
+        std::deque<bool> out(in.size());
+        std::copy(in.begin(), in.end(), out.begin());
+        assert(std::equal(in.begin(), in.end(), out.begin()));
+      }
+    }
+
+#if TEST_STD_VER >= 20
+    // join_view iterator
+    { // Copy from segmented input to contiguous output (join_viw to vector<int>)
+      std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
+      auto jv = std::ranges::join_view(v);
+      std::vector<int> expected(jv.begin(), jv.end());
+      std::vector<int> out(expected.size());
+      std::copy(jv.begin(), jv.end(), out.begin());
+      assert(out == expected);
+    }
+    // Copy from segmented input to segmented output (join_view to deque)
+    if (!TEST_IS_CONSTANT_EVALUATED) { // TODO: enable this for constant evaluation when std::deque is fully constexpr
+      std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
+      auto jv = std::ranges::join_view(v);
+      std::deque<int> expected(jv.begin(), jv.end());
+      std::deque<int> out(expected.size());
+      std::copy(jv.begin(), jv.end(), out.begin());
+      assert(out == expected);
+    }
+    { // Copy from segmented input to vector<bool> output
+      std::vector<std::vector<int>> v{{1, 1}, {1, 0, 1}, {0, 0}, {1, 1, 1}, {1}, {1, 1, 1, 1}, {0, 0, 1, 1, 0, 1, 1}};
+      auto jv = std::ranges::join_view(v);
+      std::vector<bool> expected(jv.begin(), jv.end());
+      std::vector<bool> out(expected.size());
+      std::copy(jv.begin(), jv.end(), out.begin());
+      assert(out == expected);
+    }
+#endif
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index 3b703dbe4d9b7..cf248c87fff43 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -50,7 +50,7 @@ constexpr auto wrap_input(std::vector<T>& input) {
   return std::ranges::subrange(std::move(b), std::move(e));
 }
 
-template <class Iter, class Sent = Iter>
+template <class Iter, class Sent>
 class forward_range_wrapper {
 public:
   using _Iter = forward_iterator<Iter>;
@@ -69,7 +69,7 @@ class forward_range_wrapper {
 template <class Iter, class Sent>
 forward_range_wrapper(Iter, Sent) -> forward_range_wrapper<Iter, Sent>;
 
-template <class Iter, class Sent = Iter>
+template <class Iter, class Sent>
 class random_access_range_wrapper {
 public:
   using _Iter = cpp20_random_access_iterator<Iter>;



More information about the libcxx-commits mailing list