[libcxx-commits] [libcxx] 0298e58 - [libc++] Optimize input_iterator-pair `insert` for std::vector (#113768)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 14 08:40:34 PST 2025


Author: Peng Liu
Date: 2025-01-14T11:40:29-05:00
New Revision: 0298e58c7dd1fa76b98ff270cdb9e0eba4949185

URL: https://github.com/llvm/llvm-project/commit/0298e58c7dd1fa76b98ff270cdb9e0eba4949185
DIFF: https://github.com/llvm/llvm-project/commit/0298e58c7dd1fa76b98ff270cdb9e0eba4949185.diff

LOG: [libc++] Optimize input_iterator-pair `insert` for std::vector (#113768)

As a follow-up to #113852, this PR optimizes the performance of the
`insert(const_iterator pos, InputIt first, InputIt last)` function for
`input_iterator`-pair inputs in `std::vector` for cases where
reallocation occurs during insertion. Additionally, this optimization
enhances exception safety by replacing the traditional `try-catch`
mechanism with a modern exception guard for the `insert` function.

The optimization targets cases where insertion trigger reallocation. In
scenarios without reallocation, the implementation remains unchanged.

Previous implementation
-----------------------
The previous implementation of `insert` is inefficient in reallocation
scenarios because it performs the following steps separately:
- `reserve()`: This leads to the first round of relocating old
elements to new memory;
- `rotate()`: This leads to the second round of reorganizing the
existing elements;
- Move-forward: Moves the elements after the insertion position to
their final positions.
- Insert: performs the actual insertion.

This approach results in a lot of redundant operations, requiring the
elements to undergo three rounds of relocations/reorganizations to be
placed in their final positions.

Proposed implementation
-----------------------
The proposed implementation jointly optimize the above 4 steps in the
previous implementation such that each element is placed in its final
position in just one round of relocation. Specifically, this
optimization reduces the total cost from 2 relocations + 1 std::rotate
call to just 1 relocation, without needing to call `std::rotate`,
thereby significantly improving overall performance.

Added: 
    

Modified: 
    libcxx/docs/ReleaseNotes/20.rst
    libcxx/include/__vector/vector.h
    libcxx/test/benchmarks/GenerateInput.h
    libcxx/test/benchmarks/containers/ContainerBenchmarks.h
    libcxx/test/benchmarks/containers/vector_operations.bench.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index 15940948655d7d..be330a9afc3310 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -69,9 +69,13 @@ Improvements and New Features
 - The ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY`` ABI configuration was added, which allows storing valid bounds
   in ``std::array::iterator`` and detecting OOB accesses when the appropriate hardening mode is enabled.
 
-- The input iterator overload of `assign(_InputIterator, _InputIterator)` in `std::vector<_Tp, _Allocator>` has been
-  optimized, resulting in a performance improvement of up to 2x for trivial element types (e.g., `std::vector<int>`),
-  and up to 3.4x for non-trivial element types (e.g., `std::vector<std::vector<int>>`).
+- The ``input_iterator``-pair overload of ``void assign(InputIt, InputIt)`` has been optimized for ``std::vector``, 
+  resulting in a performance improvement of up to 2x for trivial element types (e.g., ``std::vector<int>``), and up 
+  to 3.4x for non-trivial element types (e.g., ``std::vector<std::vector<int>>``).
+
+- The ``input_iterator``-pair overload of ``iterator insert(const_iterator, InputIt, InputIt)`` has been optimized 
+  for ``std::vector``, resulting in a performance improvement of up to 10x for ``std::vector<int>``, and up to 2.3x 
+  for ``std::vector<std::vector<int>>``.
 
 - On Windows, ``<system_error>``'s ``std::system_category`` is now distinct from ``std::generic_category``. The behavior
   on other operating systems is unchanged.

diff  --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index ddbf1235b90691..3a7ae531785968 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -1250,30 +1250,30 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
   
diff erence_type __off = __position - begin();
   pointer __p           = this->__begin_ + __off;
   pointer __old_last    = this->__end_;
-  for (; this->__end_ != this->__cap_ && __first != __last; ++__first) {
+  for (; this->__end_ != this->__cap_ && __first != __last; ++__first)
     __construct_one_at_end(*__first);
+
+  if (__first == __last)
+    (void)std::rotate(__p, __old_last, this->__end_);
+  else {
+    __split_buffer<value_type, allocator_type&> __v(__alloc_);
+    auto __guard = std::__make_exception_guard(
+        _AllocatorDestroyRangeReverse<allocator_type, pointer>(__alloc_, __old_last, this->__end_));
+    __v.__construct_at_end_with_sentinel(std::move(__first), std::move(__last));
+    __split_buffer<value_type, allocator_type&> __merged(
+        __recommend(size() + __v.size()), __off, __alloc_); // has `__off` positions available at the front
+    std::__uninitialized_allocator_relocate(
+        __alloc_, std::__to_address(__old_last), std::__to_address(this->__end_), std::__to_address(__merged.__end_));
+    __guard.__complete(); // Release the guard once objects in [__old_last_, __end_) have been successfully relocated.
+    __merged.__end_ += this->__end_ - __old_last;
+    this->__end_ = __old_last;
+    std::__uninitialized_allocator_relocate(
+        __alloc_, std::__to_address(__v.__begin_), std::__to_address(__v.__end_), std::__to_address(__merged.__end_));
+    __merged.__end_ += __v.size();
+    __v.__end_ = __v.__begin_;
+    __p        = __swap_out_circular_buffer(__merged, __p);
   }
-  __split_buffer<value_type, allocator_type&> __v(this->__alloc_);
-  if (__first != __last) {
-#if _LIBCPP_HAS_EXCEPTIONS
-    try {
-#endif // _LIBCPP_HAS_EXCEPTIONS
-      __v.__construct_at_end_with_sentinel(std::move(__first), std::move(__last));
-      
diff erence_type __old_size = __old_last - this->__begin_;
-      
diff erence_type __old_p    = __p - this->__begin_;
-      reserve(__recommend(size() + __v.size()));
-      __p        = this->__begin_ + __old_p;
-      __old_last = this->__begin_ + __old_size;
-#if _LIBCPP_HAS_EXCEPTIONS
-    } catch (...) {
-      erase(__make_iter(__old_last), end());
-      throw;
-    }
-#endif // _LIBCPP_HAS_EXCEPTIONS
-  }
-  __p = std::rotate(__p, __old_last, this->__end_);
-  insert(__make_iter(__p), std::make_move_iterator(__v.begin()), std::make_move_iterator(__v.end()));
-  return begin() + __off;
+  return __make_iter(__p);
 }
 
 template <class _Tp, class _Allocator>

diff  --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h
index 67ce798071eaa4..6d5c5167e91ed8 100644
--- a/libcxx/test/benchmarks/GenerateInput.h
+++ b/libcxx/test/benchmarks/GenerateInput.h
@@ -134,7 +134,14 @@ std::vector<std::vector<IntT>> getRandomIntegerInputsWithLength(std::size_t N, s
   return inputs;
 }
 
-inline std::vector<std::string> getPrefixedRandomStringInputs(std::size_t N) {
+inline std::vector<std::string> getSSORandomStringInputs(size_t N) {
+  std::vector<std::string> inputs;
+  for (size_t i = 0; i < N; ++i)
+    inputs.push_back(getRandomString(10)); // SSO
+  return inputs;
+}
+
+inline std::vector<std::string> getPrefixedRandomStringInputs(size_t N) {
   std::vector<std::string> inputs;
   inputs.reserve(N);
   constexpr int kSuffixLength = 32;

diff  --git a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
index 6d21e12896ec9e..5fc8981619672c 100644
--- a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
+++ b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
@@ -135,6 +135,66 @@ void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) {
   }
 }
 
+template <class Container, class GenInputs>
+void BM_Insert_InputIterIter_NoRealloc(benchmark::State& st, Container c, GenInputs gen) {
+  auto in = gen(st.range(0));
+  DoNotOptimizeData(in);
+  const auto size = c.size();
+  const auto beg  = cpp17_input_iterator(in.begin());
+  const auto end  = cpp17_input_iterator(in.end());
+  c.reserve(size + in.size()); // force no reallocation
+  for (auto _ : st) {
+    benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end)));
+    st.PauseTiming();
+    c.erase(c.begin() + size, c.end()); // avoid the container to grow indefinitely
+    st.ResumeTiming();
+    DoNotOptimizeData(c);
+    benchmark::ClobberMemory();
+  }
+}
+
+template <class Container, class GenInputs>
+void BM_Insert_InputIterIter_Realloc_HalfFilled(benchmark::State& st, Container, GenInputs gen) {
+  const auto size = st.range(0);
+  Container a     = gen(size);
+  Container in    = gen(size + 10);
+  DoNotOptimizeData(a);
+  DoNotOptimizeData(in);
+  const auto beg = cpp17_input_iterator(in.begin());
+  const auto end = cpp17_input_iterator(in.end());
+  for (auto _ : st) {
+    st.PauseTiming();
+    Container c;
+    c.reserve(size * 2); // Reallocation with half-filled container
+    c = a;
+    st.ResumeTiming();
+    benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end)));
+    DoNotOptimizeData(c);
+    benchmark::ClobberMemory();
+  }
+}
+
+template <class Container, class GenInputs>
+void BM_Insert_InputIterIter_Realloc_NearFull(benchmark::State& st, Container, GenInputs gen) {
+  const auto size = st.range(0);
+  Container a     = gen(size);
+  Container in    = gen(10);
+  DoNotOptimizeData(a);
+  DoNotOptimizeData(in);
+  const auto beg = cpp17_input_iterator(in.begin());
+  const auto end = cpp17_input_iterator(in.end());
+  for (auto _ : st) {
+    st.PauseTiming();
+    Container c;
+    c.reserve(size + 5); // Reallocation almost-full container
+    c = a;
+    st.ResumeTiming();
+    benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end)));
+    DoNotOptimizeData(c);
+    benchmark::ClobberMemory();
+  }
+}
+
 template <class Container, class GenInputs>
 void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) {
   auto in        = gen(st.range(0));

diff  --git a/libcxx/test/benchmarks/containers/vector_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_operations.bench.cpp
index 9449bed31ec38c..1cd754ca7e7803 100644
--- a/libcxx/test/benchmarks/containers/vector_operations.bench.cpp
+++ b/libcxx/test/benchmarks/containers/vector_operations.bench.cpp
@@ -91,4 +91,18 @@ BENCHMARK_CAPTURE(BM_AssignInputIterIter<100>,
                   getRandomIntegerInputsWithLength<int>)
     ->Args({TestNumInputs, TestNumInputs});
 
+BENCHMARK_CAPTURE(BM_Insert_InputIterIter_NoRealloc, vector_int, std::vector<int>(100, 1), getRandomIntegerInputs<int>)
+    ->Arg(514048);
+BENCHMARK_CAPTURE(
+    BM_Insert_InputIterIter_Realloc_HalfFilled, vector_int, std::vector<int>{}, getRandomIntegerInputs<int>)
+    ->Arg(514048);
+BENCHMARK_CAPTURE(BM_Insert_InputIterIter_Realloc_NearFull, vector_int, std::vector<int>{}, getRandomIntegerInputs<int>)
+    ->Arg(514048);
+BENCHMARK_CAPTURE(
+    BM_Insert_InputIterIter_Realloc_HalfFilled, vector_string, std::vector<std::string>{}, getSSORandomStringInputs)
+    ->Arg(514048);
+BENCHMARK_CAPTURE(
+    BM_Insert_InputIterIter_Realloc_NearFull, vector_string, std::vector<std::string>{}, getSSORandomStringInputs)
+    ->Arg(514048);
+
 BENCHMARK_MAIN();

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
index 934b85ce01c67b..e4625e4865c4dd 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
@@ -24,172 +24,193 @@
 namespace adl {
 struct S {};
 void make_move_iterator(S*) {}
-}
+} // namespace adl
+
+TEST_CONSTEXPR_CXX20 bool tests() {
+  //
+  // Tests for input_iterator
+  // Vector may or may not reallocate during insertion -- test both cases.
+  //
+  { // Test insertion into a vector with less spare space available than the input range, triggering reallocation
+    typedef std::vector<int> V;
+    V v(100);
+    int a[]     = {1, 2, 3, 4, 5};
+    const int N = sizeof(a) / sizeof(a[0]);
+    V::iterator i =
+        v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
+  { // Test insertion into a vector with sufficient spare space where no reallocation happens
+    typedef std::vector<int> V;
+    V v(100);
+    v.reserve(v.size() + 10);
+    int a[]     = {1, 2, 3, 4, 5};
+    const int N = sizeof(a) / sizeof(a[0]);
+    V::iterator i =
+        v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
 
-TEST_CONSTEXPR_CXX20 bool tests()
-{
-    {
-        typedef std::vector<int> V;
-        V v(100);
-        int a[] = {1, 2, 3, 4, 5};
-        const int N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a),
-                                 cpp17_input_iterator<const int*>(a+N));
-        assert(v.size() == 100 + N);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < 105; ++j)
-            assert(v[j] == 0);
-    }
-    {
-        typedef std::vector<int> V;
-        V v(100);
-        int a[] = {1, 2, 3, 4, 5};
-        const int N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
-                                 forward_iterator<const int*>(a+N));
-        assert(v.size() == 100 + N);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < 105; ++j)
-            assert(v[j] == 0);
-    }
-    {
-        typedef std::vector<int> V;
-        V v(100);
-        while(v.size() < v.capacity()) v.push_back(0); // force reallocation
-        std::size_t sz = v.size();
-        int a[] = {1, 2, 3, 4, 5};
-        const unsigned N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
-                                 forward_iterator<const int*>(a+N));
-        assert(v.size() == sz + N);
-        assert(i == v.begin() + 10);
-        std::size_t j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < v.size(); ++j)
-            assert(v[j] == 0);
-    }
-    {
-        typedef std::vector<int> V;
-        V v(100);
-        v.reserve(128); // force no reallocation
-        std::size_t sz = v.size();
-        int a[] = {1, 2, 3, 4, 5};
-        const unsigned N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
-                                 forward_iterator<const int*>(a+N));
-        assert(v.size() == sz + N);
-        assert(i == v.begin() + 10);
-        std::size_t j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < v.size(); ++j)
-            assert(v[j] == 0);
-    }
-    {
-        typedef std::vector<int, limited_allocator<int, 308> > V;
-        V v(100);
-        int a[] = {1, 2, 3, 4, 5};
-        const int N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a),
-                                 cpp17_input_iterator<const int*>(a+N));
-        assert(v.size() == 100 + N);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < 105; ++j)
-            assert(v[j] == 0);
-    }
-    {
-        typedef std::vector<int, limited_allocator<int, 300> > V;
-        V v(100);
-        int a[] = {1, 2, 3, 4, 5};
-        const int N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
-                                 forward_iterator<const int*>(a+N));
-        assert(v.size() == 100 + N);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < 105; ++j)
-            assert(v[j] == 0);
-    }
+  //
+  // Tests for forward_iterator
+  //
+  {
+    typedef std::vector<int> V;
+    V v(100);
+    int a[]       = {1, 2, 3, 4, 5};
+    const int N   = sizeof(a) / sizeof(a[0]);
+    V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
+  {
+    typedef std::vector<int> V;
+    V v(100);
+    while (v.size() < v.capacity())
+      v.push_back(0); // force reallocation
+    std::size_t sz   = v.size();
+    int a[]          = {1, 2, 3, 4, 5};
+    const unsigned N = sizeof(a) / sizeof(a[0]);
+    V::iterator i    = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+    assert(v.size() == sz + N);
+    assert(i == v.begin() + 10);
+    std::size_t j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < v.size(); ++j)
+      assert(v[j] == 0);
+  }
+  {
+    typedef std::vector<int> V;
+    V v(100);
+    v.reserve(128); // force no reallocation
+    std::size_t sz   = v.size();
+    int a[]          = {1, 2, 3, 4, 5};
+    const unsigned N = sizeof(a) / sizeof(a[0]);
+    V::iterator i    = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+    assert(v.size() == sz + N);
+    assert(i == v.begin() + 10);
+    std::size_t j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < v.size(); ++j)
+      assert(v[j] == 0);
+  }
+  {
+    typedef std::vector<int, limited_allocator<int, 308> > V;
+    V v(100);
+    int a[]     = {1, 2, 3, 4, 5};
+    const int N = sizeof(a) / sizeof(a[0]);
+    V::iterator i =
+        v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
+  {
+    typedef std::vector<int, limited_allocator<int, 300> > V;
+    V v(100);
+    int a[]       = {1, 2, 3, 4, 5};
+    const int N   = sizeof(a) / sizeof(a[0]);
+    V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
 #if TEST_STD_VER >= 11
-    {
-        typedef std::vector<int, min_allocator<int> > V;
-        V v(100);
-        int a[] = {1, 2, 3, 4, 5};
-        const int N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a),
-                                 cpp17_input_iterator<const int*>(a+N));
-        assert(v.size() == 100 + N);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < 105; ++j)
-            assert(v[j] == 0);
-    }
-    {
-        typedef std::vector<int, min_allocator<int> > V;
-        V v(100);
-        int a[] = {1, 2, 3, 4, 5};
-        const int N = sizeof(a)/sizeof(a[0]);
-        V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),
-                                 forward_iterator<const int*>(a+N));
-        assert(v.size() == 100 + N);
-        assert(is_contiguous_container_asan_correct(v));
-        assert(i == v.begin() + 10);
-        int j;
-        for (j = 0; j < 10; ++j)
-            assert(v[j] == 0);
-        for (std::size_t k = 0; k < N; ++j, ++k)
-            assert(v[j] == a[k]);
-        for (; j < 105; ++j)
-            assert(v[j] == 0);
-    }
+  {
+    typedef std::vector<int, min_allocator<int> > V;
+    V v(100);
+    int a[]     = {1, 2, 3, 4, 5};
+    const int N = sizeof(a) / sizeof(a[0]);
+    V::iterator i =
+        v.insert(v.cbegin() + 10, cpp17_input_iterator<const int*>(a), cpp17_input_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
+  {
+    typedef std::vector<int, min_allocator<int> > V;
+    V v(100);
+    int a[]       = {1, 2, 3, 4, 5};
+    const int N   = sizeof(a) / sizeof(a[0]);
+    V::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a), forward_iterator<const int*>(a + N));
+    assert(v.size() == 100 + N);
+    assert(is_contiguous_container_asan_correct(v));
+    assert(i == v.begin() + 10);
+    int j;
+    for (j = 0; j < 10; ++j)
+      assert(v[j] == 0);
+    for (std::size_t k = 0; k < N; ++j, ++k)
+      assert(v[j] == a[k]);
+    for (; j < 105; ++j)
+      assert(v[j] == 0);
+  }
 #endif
 
-    {
-        std::vector<adl::S> s;
-        s.insert(s.end(), cpp17_input_iterator<adl::S*>(nullptr), cpp17_input_iterator<adl::S*>(nullptr));
-    }
+  {
+    std::vector<adl::S> s;
+    s.insert(s.end(), cpp17_input_iterator<adl::S*>(nullptr), cpp17_input_iterator<adl::S*>(nullptr));
+  }
 
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
-    tests();
+int main(int, char**) {
+  tests();
 #if TEST_STD_VER > 17
-    static_assert(tests());
+  static_assert(tests());
 #endif
-    return 0;
+  return 0;
 }


        


More information about the libcxx-commits mailing list