diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h index 7889e8c2201ac1..02f91b537c7e22 100644 --- a/libcxx/include/__vector/vector.h +++ b/libcxx/include/__vector/vector.h @@ -1360,27 +1360,27 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu for (; this->__end_ != this->__end_cap() && __first != __last; ++__first) { __construct_one_at_end(*__first); } - __split_buffer __v(__a); - if (__first != __last) { -#if _LIBCPP_HAS_EXCEPTIONS - try { -#endif // _LIBCPP_HAS_EXCEPTIONS - __v.__construct_at_end_with_sentinel(std::move(__first), std::move(__last)); - difference_type __old_size = __old_last - this->__begin_; - difference_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 + + if (__first == __last) + (void)std::rotate(__p, __old_last, this->__end_); + else { + __split_buffer __v(__a); + auto __guard = + std::__make_exception_guard(_AllocatorDestroyRangeReverse(__a, __old_last, __end_)); + __v.__construct_at_end_with_sentinel(std::move(__first), std::move(__last)); + __split_buffer __merged(__recommend(size() + __v.size()), __off, __a); + std::__uninitialized_allocator_relocate( + __a, std::__to_address(__old_last), std::__to_address(__end_), std::__to_address(__merged.__end_)); + __merged.__end_ += __end_ - __old_last; + __end_ = __old_last; + __guard.__complete(); + std::__uninitialized_allocator_relocate( + __a, std::__to_address(__v.__begin_), std::__to_address(__v.__end_), std::__to_address(__merged.__end_)); + __merged.__end_ += __v.size(); + __v.__begin_ = __v.__end_; + __p = __swap_out_circular_buffer(__merged, __p); } - __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 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..8dce6e5c1a690e 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 @@ -46,6 +46,46 @@ TEST_CONSTEXPR_CXX20 bool tests() for (; j < 105; ++j) assert(v[j] == 0); } + { // Vector may or may not need to reallocate because of the insertion -- test both cases. + { // The input range is shorter than the remaining capacity of the vector -- ensure no reallocation happens. + typedef std::vector 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(a), cpp17_input_iterator(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); + } + { // The input range is longer than the remaining capacity of the vector -- ensure reallocation happens. + typedef std::vector V; + V v(100); + v.reserve(v.size() + 2); + 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(a), cpp17_input_iterator(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 V; V v(100);