diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -124,6 +124,8 @@ template basic_string(InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()); // constexpr since C++20 + template R> + constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // since C++23 basic_string(initializer_list, const Allocator& = Allocator()); // constexpr since C++20 basic_string(const basic_string&, const Allocator&); // constexpr since C++20 basic_string(basic_string&&, const Allocator&); // constexpr since C++20 @@ -200,6 +202,8 @@ basic_string& append(size_type n, value_type c); // constexpr since C++20 template basic_string& append(InputIterator first, InputIterator last); // constexpr since C++20 + template R> + constexpr basic_string& append_range(R&& rg); // C++23 basic_string& append(initializer_list); // constexpr since C++20 void push_back(value_type c); // constexpr since C++20 @@ -221,6 +225,8 @@ basic_string& assign(size_type n, value_type c); // constexpr since C++20 template basic_string& assign(InputIterator first, InputIterator last); // constexpr since C++20 + template R> + constexpr basic_string& assign_range(R&& rg); // C++23 basic_string& assign(initializer_list); // constexpr since C++20 basic_string& insert(size_type pos1, const basic_string& str); // constexpr since C++20 @@ -237,6 +243,8 @@ iterator insert(const_iterator p, size_type n, value_type c); // constexpr since C++20 template iterator insert(const_iterator p, InputIterator first, InputIterator last); // constexpr since C++20 + template R> + constexpr iterator insert_range(const_iterator p, R&& rg); // C++23 iterator insert(const_iterator p, initializer_list); // constexpr since C++20 basic_string& erase(size_type pos = 0, size_type n = npos); // constexpr since C++20 @@ -262,6 +270,8 @@ basic_string& replace(const_iterator i1, const_iterator i2, size_type n, value_type c); // constexpr since C++20 template basic_string& replace(const_iterator i1, const_iterator i2, InputIterator j1, InputIterator j2); // constexpr since C++20 + template R> + constexpr basic_string& replace_with_range(const_iterator i1, const_iterator i2, R&& rg); // C++23 basic_string& replace(const_iterator i1, const_iterator i2, initializer_list); // constexpr since C++20 size_type copy(value_type* s, size_type n, size_type pos = 0) const; // constexpr since C++20 @@ -354,6 +364,26 @@ char_traits::value_type>, Allocator>; // C++17 +template>> + basic_string(from_range_t, R&&, Allocator = Allocator()) + -> basic_string, char_traits>, + Allocator>; // C++23 + +template> + explicit basic_string(basic_string_view, const Allocator& = Allocator()) + -> basic_string; // C++17 + +template> + basic_string(basic_string_view, + typename see below::size_type, typename see below::size_type, + const Allocator& = Allocator()) + -> basic_string; // C++17 + template basic_string operator+(const basic_string& lhs, @@ -559,6 +589,11 @@ #include <__memory/pointer_traits.h> #include <__memory/swap_allocator.h> #include <__memory_resource/polymorphic_allocator.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/container_compatible_range.h> +#include <__ranges/from_range.h> +#include <__ranges/size.h> #include <__string/char_traits.h> #include <__string/extern_template_lists.h> #include <__type_traits/is_allocator.h> @@ -574,6 +609,7 @@ #include <__type_traits/void_t.h> #include <__utility/auto_cast.h> #include <__utility/declval.h> +#include <__utility/forward.h> #include <__utility/is_pointer_in_range.h> #include <__utility/move.h> #include <__utility/swap.h> @@ -670,6 +706,8 @@ struct __is_less_than_comparable<_Tp, _Up, __void_t() < std::declval<_Up>())> > : true_type { }; +struct __init_with_sentinel_tag {}; + template class basic_string { @@ -830,6 +868,14 @@ std::__debug_db_insert_c(this); } + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 + basic_string(__init_with_sentinel_tag, _Iter __first, _Sent __last, const allocator_type& __a) + : __r_(__default_init_tag(), __a) { + __init_with_sentinel(std::move(__first), std::move(__last)); + std::__debug_db_insert_c(this); + } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 iterator __make_iterator(pointer __p) { return iterator(this, __p); } @@ -1056,6 +1102,21 @@ std::__debug_db_insert_c(this); } +#if _LIBCPP_STD_VER >= 23 + template <_ContainerCompatibleRange<_CharT> _Range> + _LIBCPP_HIDE_FROM_ABI constexpr + basic_string(from_range_t, _Range&& __range, const allocator_type& __a = allocator_type()) + : __r_(__default_init_tag(), __a) { + if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) { + __init_with_size(ranges::begin(__range), ranges::end(__range), ranges::distance(__range)); + } else { + __init_with_sentinel(ranges::begin(__range), ranges::end(__range)); + } + + std::__debug_db_insert_c(this); + } +#endif + #ifndef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(initializer_list<_CharT> __il) : __r_(__default_init_tag(), __default_init_tag()) { @@ -1269,6 +1330,15 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(_ForwardIterator __first, _ForwardIterator __last); +#if _LIBCPP_STD_VER >= 23 + template <_ContainerCompatibleRange<_CharT> _Range> + _LIBCPP_HIDE_FROM_ABI + constexpr basic_string& append_range(_Range&& __range) { + insert_range(end(), std::forward<_Range>(__range)); + return *this; + } +#endif + #ifndef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(initializer_list __il) {return append(__il.begin(), __il.size());} @@ -1332,6 +1402,23 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(_ForwardIterator __first, _ForwardIterator __last); +#if _LIBCPP_STD_VER >= 23 + template <_ContainerCompatibleRange<_CharT> _Range> + _LIBCPP_HIDE_FROM_ABI + constexpr basic_string& assign_range(_Range&& __range) { + if constexpr (__string_is_trivial_iterator>::value && + (ranges::forward_range<_Range> || ranges::sized_range<_Range>)) { + size_type __n = static_cast(ranges::distance(__range)); + __assign_trivial(ranges::begin(__range), ranges::end(__range), __n); + + } else { + __assign_with_sentinel(ranges::begin(__range), ranges::end(__range)); + } + + return *this; + } +#endif + #ifndef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(initializer_list __il) {return assign(__il.begin(), __il.size());} @@ -1363,6 +1450,21 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, size_type __n, value_type __c); _LIBCPP_CONSTEXPR_SINCE_CXX20 iterator insert(const_iterator __pos, value_type __c); +#if _LIBCPP_STD_VER >= 23 + template <_ContainerCompatibleRange<_CharT> _Range> + _LIBCPP_HIDE_FROM_ABI + constexpr iterator insert_range(const_iterator __position, _Range&& __range) { + if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) { + auto __n = static_cast(ranges::distance(__range)); + return __insert_with_size(__position, ranges::begin(__range), ranges::end(__range), __n); + + } else { + basic_string __temp(from_range, std::forward<_Range>(__range), __alloc()); + return insert(__position, __temp.data(), __temp.data() + __temp.size()); + } + } +#endif + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 iterator insert(const_iterator __pos, size_type __n, value_type __c) { _LIBCPP_DEBUG_ASSERT(__get_const_db()->__find_c_from_i(&__pos) == this, @@ -1451,6 +1553,15 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& replace(const_iterator __i1, const_iterator __i2, _InputIterator __j1, _InputIterator __j2); +#if _LIBCPP_STD_VER >= 23 + template <_ContainerCompatibleRange<_CharT> _Range> + _LIBCPP_HIDE_FROM_ABI + constexpr basic_string& replace_with_range(const_iterator __i1, const_iterator __i2, _Range&& __range) { + basic_string __temp(from_range, std::forward<_Range>(__range), __alloc()); + return replace(__i1, __i2, __temp); + } +#endif + #ifndef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& replace(const_iterator __i1, const_iterator __i2, initializer_list __il) @@ -1709,9 +1820,17 @@ return !__libcpp_is_constant_evaluated() && (__sz < __min_cap); } - template + template + _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI + void __assign_trivial(_Iterator __first, _Sentinel __last, size_type __n); + + template + _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI + void __assign_with_sentinel(_Iterator __first, _Sentinel __last); + + template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 - iterator __insert_from_safe_copy(size_type __n, size_type __ip, _ForwardIterator __first, _ForwardIterator __last) { + iterator __insert_from_safe_copy(size_type __n, size_type __ip, _ForwardIterator __first, _Sentinel __last) { size_type __sz = size(); size_type __cap = capacity(); value_type* __p; @@ -1736,6 +1855,10 @@ return begin() + __ip; } + template + _LIBCPP_CONSTEXPR_SINCE_CXX20 + iterator __insert_with_size(const_iterator __pos, _Iterator __first, _Sentinel __last, size_type __n); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 allocator_type& __alloc() _NOEXCEPT { return __r_.second(); } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const allocator_type& __alloc() const _NOEXCEPT { return __r_.second(); } @@ -1838,6 +1961,13 @@ template ::value, int> = 0> inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void __init(_ForwardIterator __first, _ForwardIterator __last); + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 + void __init_with_sentinel(_InputIterator __first, _Sentinel __last); + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 + void __init_with_size(_InputIterator __first, _Sentinel __last, size_type __sz); + _LIBCPP_CONSTEXPR_SINCE_CXX20 void __grow_by(size_type __old_cap, size_type __delta_cap, size_type __old_sz, size_type __n_copy, size_type __n_del, size_type __n_add = 0); @@ -2033,6 +2163,15 @@ -> basic_string<_CharT, _Traits, _Allocator>; #endif +#if _LIBCPP_STD_VER >= 23 +template >, + class = enable_if_t<__is_allocator<_Allocator>::value> + > +basic_string(from_range_t, _Range&&, _Allocator = _Allocator()) + -> basic_string, char_traits>, _Allocator>; +#endif + template inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void @@ -2178,7 +2317,15 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__init(_InputIterator __first, _InputIterator __last) { + __init_with_sentinel(std::move(__first), std::move(__last)); +} + +template +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 +void basic_string<_CharT, _Traits, _Allocator>::__init_with_sentinel(_InputIterator __first, _Sentinel __last) { __default_init(); + #ifndef _LIBCPP_HAS_NO_EXCEPTIONS try { @@ -2201,16 +2348,27 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__init(_ForwardIterator __first, _ForwardIterator __last) { + size_type __sz = static_cast(std::distance(__first, __last)); + __init_with_size(__first, __last, __sz); +} + +template +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 +void basic_string<_CharT, _Traits, _Allocator>::__init_with_size( + _InputIterator __first, _Sentinel __last, size_type __sz) { if (__libcpp_is_constant_evaluated()) __r_.first() = __rep(); - size_type __sz = static_cast(std::distance(__first, __last)); + if (__sz > max_size()) __throw_length_error(); + pointer __p; if (__fits_in_sso(__sz)) { __set_short_size(__sz); __p = __get_short_pointer(); + } else { @@ -2466,9 +2624,17 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::assign(_InputIterator __first, _InputIterator __last) { - const basic_string __temp(__first, __last, __alloc()); - assign(__temp.data(), __temp.size()); - return *this; + __assign_with_sentinel(__first, __last); + return *this; +} + +template +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 +void +basic_string<_CharT, _Traits, _Allocator>::__assign_with_sentinel(_InputIterator __first, _Sentinel __last) { + const basic_string __temp(__init_with_sentinel_tag(), std::move(__first), std::move(__last), __alloc()); + assign(__temp.data(), __temp.size()); } template @@ -2476,31 +2642,42 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last) { - size_type __cap = capacity(); - size_type __n = __string_is_trivial_iterator<_ForwardIterator>::value ? - static_cast(std::distance(__first, __last)) : 0; + if (__string_is_trivial_iterator<_ForwardIterator>::value) { + size_type __n = static_cast(std::distance(__first, __last)); + __assign_trivial(__first, __last, __n); + } else { + __assign_with_sentinel(__first, __last); + } - if (__string_is_trivial_iterator<_ForwardIterator>::value && - (__cap >= __n || !__addr_in_range(*__first))) - { - if (__cap < __n) - { - size_type __sz = size(); - __grow_by(__cap, __n - __cap, __sz, 0, __sz); - } - pointer __p = __get_pointer(); - for (; __first != __last; ++__p, (void) ++__first) - traits_type::assign(*__p, *__first); - traits_type::assign(*__p, value_type()); - __set_size(__n); - __invalidate_iterators_past(__n); - } - else + return *this; +} + +template +template +_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI +void +basic_string<_CharT, _Traits, _Allocator>::__assign_trivial(_Iterator __first, _Sentinel __last, size_type __n) { + _LIBCPP_ASSERT(__string_is_trivial_iterator<_Iterator>::value, + "The iterator type given to `__assign_trivial` must be trivial"); + + size_type __cap = capacity(); + if (__cap < __n) { - const basic_string __temp(__first, __last, __alloc()); - assign(__temp.data(), __temp.size()); + // Unlike `append` functions, if the input range points into the string itself, there is no case that the input + // range could get invalidated by reallocation: + // 1. If the input range is a subset of the string itself, its size cannot exceed the capacity of the string, + // thus no reallocation would happen. + // 2. In the exotic case where the input range is the byte representation of the string itself, the string + // object itself stays valid even if reallocation happens. + size_type __sz = size(); + __grow_by(__cap, __n - __cap, __sz, 0, __sz); } - return *this; + pointer __p = __get_pointer(); + for (; __first != __last; ++__p, (void) ++__first) + traits_type::assign(*__p, *__first); + traits_type::assign(*__p, value_type()); + __set_size(__n); + __invalidate_iterators_past(__n); } template @@ -2806,19 +2983,27 @@ { _LIBCPP_DEBUG_ASSERT(__get_const_db()->__find_c_from_i(&__pos) == this, "string::insert(iterator, range) called with an iterator not referring to this string"); + auto __n = static_cast(std::distance(__first, __last)); + return __insert_with_size(__pos, __first, __last, __n); +} +template +template +_LIBCPP_CONSTEXPR_SINCE_CXX20 +typename basic_string<_CharT, _Traits, _Allocator>::iterator +basic_string<_CharT, _Traits, _Allocator>::__insert_with_size( + const_iterator __pos, _Iterator __first, _Sentinel __last, size_type __n) { size_type __ip = static_cast(__pos - begin()); - size_type __n = static_cast(std::distance(__first, __last)); if (__n == 0) return begin() + __ip; - if (__string_is_trivial_iterator<_ForwardIterator>::value && !__addr_in_range(*__first)) + if (__string_is_trivial_iterator<_Iterator>::value && !__addr_in_range(*__first)) { return __insert_from_safe_copy(__n, __ip, __first, __last); } else { - const basic_string __temp(__first, __last, __alloc()); + const basic_string __temp(__init_with_sentinel_tag(), __first, __last, __alloc()); return __insert_from_safe_copy(__n, __ip, __temp.begin(), __temp.end()); } } diff --git a/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template R> +// basic_string(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23 + +#include +#include +#include +#include + +#include "../../../containers/from_range_helpers.h" +#include "../../../containers/sequences/from_range_sequence_containers.h" +#include "test_macros.h" + +std::vector long_string = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' +}; + +template +concept StringHasFromRangeAllocCtr = requires (Range&& range, const Alloc& alloc) { + Container(std::from_range, std::forward(range), alloc); +}; + +constexpr bool test_constraints() { + // (from_range, range) + // + // Input range with the same value type. + static_assert(HasFromRangeCtr>); + // Input range with a convertible value type. + static_assert(HasFromRangeCtr>); + // Input range with a non-convertible value type. + static_assert(!HasFromRangeCtr>); + // Not an input range. + static_assert(!HasFromRangeCtr); + static_assert(!HasFromRangeCtr); + static_assert(!HasFromRangeCtr); + + // (from_range, range, alloc) + // + // Input range with the same value type. + using Alloc = test_allocator; + using StringWithAlloc = std::basic_string, Alloc>; + static_assert(StringHasFromRangeAllocCtr, Alloc>); + // Input range with a convertible value type. + static_assert(StringHasFromRangeAllocCtr, Alloc>); + // Input range with a non-convertible value type. + static_assert(!StringHasFromRangeAllocCtr, Alloc>); + // Not an input range. + static_assert(!StringHasFromRangeAllocCtr); + static_assert(!StringHasFromRangeAllocCtr); + static_assert(!StringHasFromRangeAllocCtr); + // Not an allocator. + static_assert(!StringHasFromRangeAllocCtr, Empty>); + + return true; +} + +template +constexpr void test_with_input(std::vector input) { + auto b = Iter(input.data()); + auto e = Iter(input.data() + input.size()); + std::ranges::subrange in(std::move(b), Sent(std::move(e))); + + { // (range) + std::string c(std::from_range, in); + + LIBCPP_ASSERT(c.__invariants()); + assert(c.size() == static_cast(std::distance(c.begin(), c.end()))); + assert(std::ranges::equal(in, c)); + } + + { // (range, allocator) + Alloc alloc; + std::basic_string, Alloc> c(std::from_range, in, alloc); + + LIBCPP_ASSERT(c.__invariants()); + assert(c.get_allocator() == alloc); + assert(c.size() == static_cast(std::distance(c.begin(), c.end()))); + assert(std::ranges::equal(in, c)); + } +} + +void test_string_exception_safety_throwing_allocator() { +#if !defined(TEST_HAS_NO_EXCEPTIONS) + try { + ThrowingAllocator alloc; + + globalMemCounter.reset(); + // Note: the input string must be long enough to prevent SSO, otherwise the allocator won't be used. + std::basic_string, ThrowingAllocator> c(std::from_range, long_string, alloc); + assert(false); // The constructor call should throw. + + } catch (int) { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + } +#endif +} + +int main(int, char**) { + for_all_iterators_and_allocators([]() { + // Shorter input -- SSO. + test_with_input({'a', 'b', 'c', 'd', 'e'}); + + // Longer input -- no SSO. + test_with_input(long_string); + + // Empty input. + test_with_input({}); + + // Single-element input. + test_with_input({'a'}); + }); + + static_assert(test_constraints()); + + // Note: `test_exception_safety_throwing_copy` doesn't apply because copying a `char` cannot throw. + test_string_exception_safety_throwing_allocator(); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.cons/from_range_deduction.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/from_range_deduction.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.cons/from_range_deduction.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// To silence a GCC warning-turned-error re. `BadAlloc::value_type`. +// ADDITIONAL_COMPILE_FLAGS: -Wno-unused-local-typedefs + +// template>> +// basic_string(from_range_t, R&&, Allocator = Allocator()) +// -> basic_string, char_traits>, +// Allocator>; // C++23 +// +// The deduction guide shall not participate in overload resolution if Allocator +// is a type that does not qualify as an allocator (in addition to the `input_range` concept being satisfied by `R`). + +#include +#include + +#include "deduction_guides_sfinae_checks.h" +#include "test_allocator.h" + +int main(int, char**) { + using Char = char16_t; + + { + std::basic_string c(std::from_range, std::array()); + static_assert(std::is_same_v>); + } + + { + using Alloc = test_allocator; + std::basic_string c(std::from_range, std::array(), Alloc()); + static_assert(std::is_same_v, Alloc>>); + } + + // Note: defining `value_type` is a workaround because one of the deduction guides will end up instantiating + // `basic_string`, and that would fail with a hard error if the given allocator doesn't define `value_type`. + struct BadAlloc { using value_type = char; }; + SequenceContainerDeductionGuidesSfinaeAway, BadAlloc>(); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp --- a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc_deduction.pass.cpp @@ -47,6 +47,7 @@ static_assert( CanDeduce>::value); static_assert(!CanDeduce>::value); static_assert(!CanDeduce>::value); +static_assert(!CanDeduce>::value); #ifndef TEST_HAS_NO_WIDE_CHARACTERS static_assert( CanDeduce>::value); static_assert(!CanDeduce>::value); diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/append_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/append_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/append_range.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template R> +// constexpr basic_string& append_range(R&& rg); // C++23 + +#include + +#include "../../../../containers/sequences/insert_range_sequence_containers.h" +#include "test_macros.h" + +void test_string_append_range_exception_safety_throwing_allocator() { +#if !defined(TEST_HAS_NO_EXCEPTIONS) + // Note: the input string must be long enough to prevent SSO, otherwise the allocator won't be used. + std::string in(64, 'a'); + + try { + ThrowingAllocator alloc; + + globalMemCounter.reset(); + std::basic_string, ThrowingAllocator> c(alloc); + c.append_range(in); + assert(false); // The function call above should throw. + + } catch (int) { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + } +#endif +} + +// Tested cases: +// - different kinds of insertions (appending an {empty/one-element/mid-sized/long range} into an +// {empty/one-element/full} container); +// - an exception is thrown when allocating new elements. +int main(int, char**) { + static_assert(test_constraints_append_range()); + + for_all_iterators_and_allocators([]() { + test_sequence_append_range, Alloc>, Iter, Sent>([](auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); + }); + + { // Check that `append_range` returns a reference to the string. + std::string c; + static_assert(std::is_lvalue_reference_v.input) + )>); + assert(&c.append_range(FullContainer_Begin_EmptyRange.input) == &c); + assert(&c.append_range(FullContainer_Begin_OneElementRange.input) == &c); + assert(&c.append_range(FullContainer_Begin_MidRange.input) == &c); + assert(&c.append_range(FullContainer_Begin_LongRange.input) == &c); + } + + // Note: `test_append_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot throw. + test_string_append_range_exception_safety_throwing_allocator(); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/assign_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/assign_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/assign_range.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template R> +// constexpr basic_string& assign_range(R&& rg); // C++23 + +#include + +#include "../../../../containers/sequences/insert_range_sequence_containers.h" +#include "test_macros.h" + +void test_string_assign_range_exception_safety_throwing_allocator() { +#if !defined(TEST_HAS_NO_EXCEPTIONS) + // Note: the input string must be long enough to prevent SSO, otherwise the allocator won't be used. + std::string in(64, 'a'); + + try { + ThrowingAllocator alloc; + + globalMemCounter.reset(); + std::basic_string, ThrowingAllocator> c(alloc); + c.assign_range(in); + assert(false); // The function call above should throw. + + } catch (int) { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + } +#endif +} + +// Tested cases: +// - different kinds of assignments (assigning an {empty/one-element/mid-sized/long range} to an +// {empty/one-element/full} container); +// - an exception is thrown when allocating new elements. +int main(int, char**) { + static_assert(test_constraints_assign_range()); + + for_all_iterators_and_allocators([]() { + test_sequence_assign_range, Alloc>, Iter, Sent>([](auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); + }); + + { // Check that `assign_range` returns a reference to the string. + std::string c; + static_assert(std::is_lvalue_reference_v.input) + )>); + assert(&c.assign_range(FullContainer_Begin_EmptyRange.input) == &c); + assert(&c.assign_range(FullContainer_Begin_OneElementRange.input) == &c); + assert(&c.assign_range(FullContainer_Begin_MidRange.input) == &c); + assert(&c.assign_range(FullContainer_Begin_LongRange.input) == &c); + } + + // Note: `test_assign_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot throw. + test_string_assign_range_exception_safety_throwing_allocator(); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template R> +// constexpr iterator insert_range(const_iterator p, R&& rg); // C++23 + +#include + +#include "../../../../containers/sequences/insert_range_sequence_containers.h" +#include "test_macros.h" + +void test_string_insert_range_exception_safety_throwing_allocator() { +#if !defined(TEST_HAS_NO_EXCEPTIONS) + // Note: the input string must be long enough to prevent SSO, otherwise the allocator won't be used. + std::string in(64, 'a'); + + try { + ThrowingAllocator alloc; + + globalMemCounter.reset(); + std::basic_string, ThrowingAllocator> c(alloc); + c.insert_range(c.end(), in); + assert(false); // The function call above should throw. + + } catch (int) { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + } +#endif +} + +// Tested cases: +// - different kinds of insertions (inserting an {empty/one-element/mid-sized/long range} into an +// {empty/one-element/full} container at the {beginning/middle/end}); +// - an exception is thrown when allocating new elements. +int main(int, char**) { + static_assert(test_constraints_insert_range()); + + for_all_iterators_and_allocators([]() { + test_sequence_insert_range, Alloc>, Iter, Sent>([](auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); + }); + + // Note: `test_insert_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot throw. + test_string_insert_range_exception_safety_throwing_allocator(); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/replace_with_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/replace_with_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/replace_with_range.pass.cpp @@ -0,0 +1,918 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=10000000 +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=70000000 + +// template R> +// constexpr basic_string& replace_with_range(const_iterator i1, const_iterator i2, R&& rg); // C++23 + +#include +#include + +#include "../../../../containers/sequences/insert_range_sequence_containers.h" +#include "test_macros.h" + +template +concept HasReplaceWithRange = requires (std::string& c, Range&& range) { + c.replace_with_range(c.end(), c.end(), std::forward(range)); +}; + +constexpr bool test_constraints_replace_with_range() { + // Input range with the same value type. + static_assert(HasReplaceWithRange>); + // Input range with a convertible value type. + static_assert(HasReplaceWithRange>); + // Input range with a non-convertible value type. + static_assert(!HasReplaceWithRange>); + // Not an input range. + static_assert(!HasReplaceWithRange); + static_assert(!HasReplaceWithRange); + static_assert(!HasReplaceWithRange); + + return true; +} + +using StrBuffer = Buffer; + +struct TestCaseReplacement { + StrBuffer initial; + std::size_t from = 0; + std::size_t to = 0; + StrBuffer input; + StrBuffer expected; +}; + +// Permutation matrix: +// - initial string: empty / one-element / n elements; +// - cut starts from: beginning / middle / end; +// - cut size: empty / one-element / several elements / until the end; +// - input range: empty / one-element / middle-sized / longer than SSO / longer than the current string capacity. + +// Empty string. + +constexpr TestCaseReplacement EmptyString_End_EmptyCut_EmptyRange { + .initial = "", .from = 0, .to = 0, .input = "", .expected = "" +}; + +constexpr TestCaseReplacement EmptyString_End_EmptyCut_OneElementRange { + .initial = "", .from = 0, .to = 0, .input = "a", .expected = "a" +}; + +constexpr TestCaseReplacement EmptyString_End_EmptyCut_MidRange { + .initial = "", .from = 0, .to = 0, .input = "aeiou", .expected = "aeiou" +}; + +constexpr TestCaseReplacement EmptyString_End_EmptyCut_LongRange { + .initial = "", .from = 0, .to = 0, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +// One-element string. + +constexpr TestCaseReplacement OneElementString_Begin_EmptyCut_EmptyRange { + .initial = "B", .from = 0, .to = 0, .input = "", .expected = "B" +}; + +constexpr TestCaseReplacement OneElementString_Begin_EmptyCut_OneElementRange { + .initial = "B", .from = 0, .to = 0, .input = "a", .expected = "aB" +}; + +constexpr TestCaseReplacement OneElementString_Begin_EmptyCut_MidRange { + .initial = "B", .from = 0, .to = 0, .input = "aeiou", .expected = "aeiouB" +}; + +constexpr TestCaseReplacement OneElementString_Begin_EmptyCut_LongRange { + .initial = "B", .from = 0, .to = 0, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789B" +}; + +constexpr TestCaseReplacement OneElementString_Begin_OneElementCut_EmptyRange { + .initial = "B", .from = 0, .to = 1, .input = "", .expected = "" +}; + +constexpr TestCaseReplacement OneElementString_Begin_OneElementCut_OneElementRange { + .initial = "B", .from = 0, .to = 1, .input = "a", .expected = "a" +}; + +constexpr TestCaseReplacement OneElementString_Begin_OneElementCut_MidRange { + .initial = "B", .from = 0, .to = 1, .input = "aeiou", .expected = "aeiou" +}; + +constexpr TestCaseReplacement OneElementString_Begin_OneElementCut_LongRange { + .initial = "B", .from = 0, .to = 1, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +constexpr TestCaseReplacement OneElementString_End_EmptyCut_EmptyRange { + .initial = "B", .from = 1, .to = 1, .input = "", .expected = "B" +}; + +constexpr TestCaseReplacement OneElementString_End_EmptyCut_OneElementRange { + .initial = "B", .from = 1, .to = 1, .input = "a", .expected = "Ba" +}; + +constexpr TestCaseReplacement OneElementString_End_EmptyCut_MidRange { + .initial = "B", .from = 1, .to = 1, .input = "aeiou", .expected = "Baeiou" +}; + +constexpr TestCaseReplacement OneElementString_End_EmptyCut_LongRange { + .initial = "B", .from = 1, .to = 1, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "Babcdefghijklmnopqrstuvwxyz0123456789" +}; + +// Short string (using SSO). + +// Replace at the beginning. + +constexpr TestCaseReplacement ShortString_Begin_EmptyCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 0, .input = "", .expected = "_BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_EmptyCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 0, .input = "a", .expected = "a_BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_EmptyCut_MidRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 0, .input = "aeiou", .expected = "aeiou_BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_EmptyCut_LongRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 0, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789_BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_OneElementCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 1, .input = "", .expected = "BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_OneElementCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 1, .input = "a", .expected = "aBCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_OneElementCut_MidRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 1, .input = "aeiou", .expected = "aeiouBCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_OneElementCut_LongRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 1, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_MidSizedCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 3, .input = "", .expected = "DFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_MidSizedCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 3, .input = "a", .expected = "aDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_MidSizedCut_MidRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 3, .input = "aeiou", .expected = "aeiouDFGHJ_" +}; + +// Note: this test case switches from SSO to non-SSO. +constexpr TestCaseReplacement ShortString_Begin_MidSizedCut_LongRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 3, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789DFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Begin_CutToEnd_EmptyRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 9, .input = "", .expected = "" +}; + +constexpr TestCaseReplacement ShortString_Begin_CutToEnd_OneElementRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 9, .input = "a", .expected = "a" +}; + +constexpr TestCaseReplacement ShortString_Begin_CutToEnd_MidRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 9, .input = "aeiou", .expected = "aeiou" +}; + +// Note: this test case switches from SSO to non-SSO. +constexpr TestCaseReplacement ShortString_Begin_CutToEnd_LongRange { + .initial = "_BCDFGHJ_", .from = 0, .to = 9, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +// Replace in the middle. + +constexpr TestCaseReplacement ShortString_Mid_EmptyCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 4, .input = "", .expected = "_BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_EmptyCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 4, .input = "a", .expected = "_BCDaFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_EmptyCut_MidRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 4, .input = "aeiou", .expected = "_BCDaeiouFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_EmptyCut_LongRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 4, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_BCDabcdefghijklmnopqrstuvwxyz0123456789FGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_OneElementCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 5, .input = "", .expected = "_BCDGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_OneElementCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 5, .input = "a", .expected = "_BCDaGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_OneElementCut_MidRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 5, .input = "aeiou", .expected = "_BCDaeiouGHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_OneElementCut_LongRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 5, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_BCDabcdefghijklmnopqrstuvwxyz0123456789GHJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_MidSizedCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 7, .input = "", .expected = "_BCDJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_MidSizedCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 7, .input = "a", .expected = "_BCDaJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_MidSizedCut_MidRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 7, .input = "aeiou", .expected = "_BCDaeiouJ_" +}; + +constexpr TestCaseReplacement ShortString_Mid_MidSizedCut_LongRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 7, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_BCDabcdefghijklmnopqrstuvwxyz0123456789J_" +}; + +constexpr TestCaseReplacement ShortString_Mid_CutToEnd_EmptyRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 9, .input = "", .expected = "_BCD" +}; + +constexpr TestCaseReplacement ShortString_Mid_CutToEnd_OneElementRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 9, .input = "a", .expected = "_BCDa" +}; + +constexpr TestCaseReplacement ShortString_Mid_CutToEnd_MidRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 9, .input = "aeiou", .expected = "_BCDaeiou" +}; + +constexpr TestCaseReplacement ShortString_Mid_CutToEnd_LongRange { + .initial = "_BCDFGHJ_", .from = 4, .to = 9, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_BCDabcdefghijklmnopqrstuvwxyz0123456789" +}; + +// Replace at the end. + +constexpr TestCaseReplacement ShortString_End_EmptyCut_EmptyRange { + .initial = "_BCDFGHJ_", .from = 9, .to = 9, .input = "", .expected = "_BCDFGHJ_" +}; + +constexpr TestCaseReplacement ShortString_End_EmptyCut_OneElementRange { + .initial = "_BCDFGHJ_", .from = 9, .to = 9, .input = "a", .expected = "_BCDFGHJ_a" +}; + +constexpr TestCaseReplacement ShortString_End_EmptyCut_MidRange { + .initial = "_BCDFGHJ_", .from = 9, .to = 9, .input = "aeiou", .expected = "_BCDFGHJ_aeiou" +}; + +constexpr TestCaseReplacement ShortString_End_EmptyCut_LongRange { + .initial = "_BCDFGHJ_", .from = 9, .to = 9, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_BCDFGHJ_abcdefghijklmnopqrstuvwxyz0123456789" +}; + +// Long string (no SSO). + +// Replace at the beginning. + +constexpr TestCaseReplacement LongString_Begin_EmptyCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 0, .input = "", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_EmptyCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 0, .input = "a", + .expected = "a_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_EmptyCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 0, .input = "aeiou", + .expected = "aeiou_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_EmptyCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 0, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_EmptyCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 0, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" + "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_OneElementCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 1, .input = "", + .expected = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_OneElementCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 1, .input = "a", + .expected = "aABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_OneElementCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 1, .input = "aeiou", + .expected = "aeiouABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_OneElementCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 1, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_OneElementCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 1, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_MidSizedCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 3, .input = "", + .expected = "CDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_MidSizedCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 3, .input = "a", + .expected = "aCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_MidSizedCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 3, .input = "aeiou", + .expected = "aeiouCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_MidSizedCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 3, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789CDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_MidSizedCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 3, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" + "CDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Begin_CutToEnd_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 38, .input = "", + .expected = "" +}; + +constexpr TestCaseReplacement LongString_Begin_CutToEnd_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 38, .input = "a", + .expected = "a" +}; + +constexpr TestCaseReplacement LongString_Begin_CutToEnd_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 38, .input = "aeiou", + .expected = "aeiou" +}; + +constexpr TestCaseReplacement LongString_Begin_CutToEnd_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 38, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +constexpr TestCaseReplacement LongString_Begin_CutToEnd_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 0, .to = 38, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +// Replace in the middle. + +constexpr TestCaseReplacement LongString_Mid_EmptyCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 18, .input = "", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_EmptyCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 18, .input = "a", + .expected = "_ABCDEFGHIJKLMNOPQaRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_EmptyCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 18, .input = "aeiou", + .expected = "_ABCDEFGHIJKLMNOPQaeiouRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_EmptyCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 18, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQabcdefghijklmnopqrstuvwxyz0123456789RSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_EmptyCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 18, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQ" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" + "RSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_OneElementCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 19, .input = "", + .expected = "_ABCDEFGHIJKLMNOPQSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_OneElementCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 19, .input = "a", + .expected = "_ABCDEFGHIJKLMNOPQaSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_OneElementCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 19, .input = "aeiou", + .expected = "_ABCDEFGHIJKLMNOPQaeiouSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_OneElementCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 19, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQabcdefghijklmnopqrstuvwxyz0123456789STUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_OneElementCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 19, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQ" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" + "STUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_MidSizedCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 21, .input = "", + .expected = "_ABCDEFGHIJKLMNOPQUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_MidSizedCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 21, .input = "a", + .expected = "_ABCDEFGHIJKLMNOPQaUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_MidSizedCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 21, .input = "aeiou", + .expected = "_ABCDEFGHIJKLMNOPQaeiouUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_MidSizedCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 21, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQabcdefghijklmnopqrstuvwxyz0123456789UVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_MidSizedCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 21, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQ" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" + "UVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_Mid_CutToEnd_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 38, .input = "", + .expected = "_ABCDEFGHIJKLMNOPQ" +}; + +constexpr TestCaseReplacement LongString_Mid_CutToEnd_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 38, .input = "a", + .expected = "_ABCDEFGHIJKLMNOPQa" +}; + +constexpr TestCaseReplacement LongString_Mid_CutToEnd_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 38, .input = "aeiou", + .expected = "_ABCDEFGHIJKLMNOPQaeiou" +}; + +constexpr TestCaseReplacement LongString_Mid_CutToEnd_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 38, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQabcdefghijklmnopqrstuvwxyz0123456789" +}; + +constexpr TestCaseReplacement LongString_Mid_CutToEnd_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 18, .to = 38, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQ" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +// Replace at the end. + +constexpr TestCaseReplacement LongString_End_EmptyCut_EmptyRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 38, .to = 38, .input = "", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +}; + +constexpr TestCaseReplacement LongString_End_EmptyCut_OneElementRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 38, .to = 38, .input = "a", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_a" +}; + +constexpr TestCaseReplacement LongString_End_EmptyCut_MidRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 38, .to = 38, .input = "aeiou", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_aeiou" +}; + +constexpr TestCaseReplacement LongString_End_EmptyCut_LongRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 38, .to = 38, + .input = "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_abcdefghijklmnopqrstuvwxyz0123456789" +}; + +constexpr TestCaseReplacement LongString_End_EmptyCut_LongerThanCapacityRange { + .initial = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", .from = 38, .to = 38, + .input = "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789", + .expected = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789_" + "abcdefghijklmnopqrstuvwxyz0123456789" +}; + +template +constexpr void test_string_replace_with_range() { + auto test = [&](const TestCaseReplacement& test_case) { + using Container = std::basic_string, Alloc>; + + auto get_pos = [](auto& c, auto index) { return std::ranges::next(c.begin(), index); }; + Container c(test_case.initial.begin(), test_case.initial.end()); + auto in = wrap_input(test_case.input); + auto from = get_pos(c, test_case.from); + auto to = get_pos(c, test_case.to); + + Container& result = c.replace_with_range(from, to, in); + assert(&result == &c); + LIBCPP_ASSERT(c.__invariants()); + return std::ranges::equal(c, test_case.expected); + }; + + { // Empty string. + // empty_str.replace_with_range(end, end, empty_range) + assert(test(EmptyString_End_EmptyCut_EmptyRange)); + // empty_str.replace_with_range(end, end, one_element_range) + assert(test(EmptyString_End_EmptyCut_OneElementRange)); + // empty_str.replace_with_range(end, end, mid_range) + assert(test(EmptyString_End_EmptyCut_MidRange)); + // empty_str.replace_with_range(end, end, long_range) + assert(test(EmptyString_End_EmptyCut_LongRange)); + } + + { // One-element string. + // one_element_str.replace_with_range(begin, begin, empty_range) + assert(test(OneElementString_Begin_EmptyCut_EmptyRange)); + // one_element_str.replace_with_range(begin, begin, one_element_range) + assert(test(OneElementString_Begin_EmptyCut_OneElementRange)); + // one_element_str.replace_with_range(begin, begin, mid_range) + assert(test(OneElementString_Begin_EmptyCut_MidRange)); + // one_element_str.replace_with_range(begin, begin, long_range) + assert(test(OneElementString_Begin_EmptyCut_LongRange)); + // one_element_str.replace_with_range(begin, begin + 1, empty_range) + assert(test(OneElementString_Begin_OneElementCut_EmptyRange)); + // one_element_str.replace_with_range(begin, begin + 1, one_element_range) + assert(test(OneElementString_Begin_OneElementCut_OneElementRange)); + // one_element_str.replace_with_range(begin, begin + 1, mid_range) + assert(test(OneElementString_Begin_OneElementCut_MidRange)); + // one_element_str.replace_with_range(begin, begin + 1, long_range) + assert(test(OneElementString_Begin_OneElementCut_LongRange)); + // one_element_str.replace_with_range(end, end, empty_range) + assert(test(OneElementString_End_EmptyCut_EmptyRange)); + // one_element_str.replace_with_range(end, end, one_element_range) + assert(test(OneElementString_End_EmptyCut_OneElementRange)); + // one_element_str.replace_with_range(end, end, mid_range) + assert(test(OneElementString_End_EmptyCut_MidRange)); + // one_element_str.replace_with_range(end, end, long_range) + assert(test(OneElementString_End_EmptyCut_LongRange)); + } + + { // Short string. + // Replace at the beginning. + + // short_str.replace_with_range(begin, begin, empty_range) + assert(test(ShortString_Begin_EmptyCut_EmptyRange)); + // short_str.replace_with_range(begin, begin, one_element_range) + assert(test(ShortString_Begin_EmptyCut_OneElementRange)); + // short_str.replace_with_range(begin, begin, mid_range) + assert(test(ShortString_Begin_EmptyCut_MidRange)); + // short_str.replace_with_range(begin, begin, long_range) + assert(test(ShortString_Begin_EmptyCut_LongRange)); + // short_str.replace_with_range(begin, begin + 1, empty_range) + assert(test(ShortString_Begin_OneElementCut_EmptyRange)); + // short_str.replace_with_range(begin, begin + 1, one_element_range) + assert(test(ShortString_Begin_OneElementCut_OneElementRange)); + // short_str.replace_with_range(begin, begin + 1, mid_range) + assert(test(ShortString_Begin_OneElementCut_MidRange)); + // short_str.replace_with_range(begin, begin + 1, long_range) + assert(test(ShortString_Begin_OneElementCut_LongRange)); + // short_str.replace_with_range(begin, begin + 3, empty_range) + assert(test(ShortString_Begin_MidSizedCut_EmptyRange)); + // short_str.replace_with_range(begin, begin + 3, one_element_range) + assert(test(ShortString_Begin_MidSizedCut_OneElementRange)); + // short_str.replace_with_range(begin, begin + 3, mid_range) + assert(test(ShortString_Begin_MidSizedCut_MidRange)); + // short_str.replace_with_range(begin, begin + 3, long_range) + assert(test(ShortString_Begin_MidSizedCut_LongRange)); + // short_str.replace_with_range(begin, end, empty_range) + assert(test(ShortString_Begin_CutToEnd_EmptyRange)); + // short_str.replace_with_range(begin, end, one_element_range) + assert(test(ShortString_Begin_CutToEnd_OneElementRange)); + // short_str.replace_with_range(begin, end, mid_range) + assert(test(ShortString_Begin_CutToEnd_MidRange)); + // short_str.replace_with_range(begin, end, long_range) + assert(test(ShortString_Begin_CutToEnd_LongRange)); + + // Replace in the middle. + + // short_str.replace_with_range(mid, mid, empty_range) + assert(test(ShortString_Mid_EmptyCut_EmptyRange)); + // short_str.replace_with_range(mid, mid, one_element_range) + assert(test(ShortString_Mid_EmptyCut_OneElementRange)); + // short_str.replace_with_range(mid, mid, mid_range) + assert(test(ShortString_Mid_EmptyCut_MidRange)); + // short_str.replace_with_range(mid, mid, long_range) + assert(test(ShortString_Mid_EmptyCut_LongRange)); + // short_str.replace_with_range(mid, mid + 1, empty_range) + assert(test(ShortString_Mid_OneElementCut_EmptyRange)); + // short_str.replace_with_range(mid, mid + 1, one_element_range) + assert(test(ShortString_Mid_OneElementCut_OneElementRange)); + // short_str.replace_with_range(mid, mid + 1, mid_range) + assert(test(ShortString_Mid_OneElementCut_MidRange)); + // short_str.replace_with_range(mid, mid + 1, long_range) + assert(test(ShortString_Mid_OneElementCut_LongRange)); + // short_str.replace_with_range(mid, mid + 3, empty_range) + assert(test(ShortString_Mid_MidSizedCut_EmptyRange)); + // short_str.replace_with_range(mid, mid + 3, one_element_range) + assert(test(ShortString_Mid_MidSizedCut_OneElementRange)); + // short_str.replace_with_range(mid, mid + 3, mid_range) + assert(test(ShortString_Mid_MidSizedCut_MidRange)); + // short_str.replace_with_range(mid, mid + 3, long_range) + assert(test(ShortString_Mid_MidSizedCut_LongRange)); + // short_str.replace_with_range(mid, end, empty_range) + assert(test(ShortString_Mid_CutToEnd_EmptyRange)); + // short_str.replace_with_range(mid, end, one_element_range) + assert(test(ShortString_Mid_CutToEnd_OneElementRange)); + // short_str.replace_with_range(mid, end, mid_range) + assert(test(ShortString_Mid_CutToEnd_MidRange)); + // short_str.replace_with_range(mid, end, long_range) + assert(test(ShortString_Mid_CutToEnd_LongRange)); + + // Replace at the end. + + // short_str.replace_with_range(end, end, empty_range) + assert(test(ShortString_End_EmptyCut_EmptyRange)); + // short_str.replace_with_range(end, end, one_element_range) + assert(test(ShortString_End_EmptyCut_OneElementRange)); + // short_str.replace_with_range(end, end, mid_range) + assert(test(ShortString_End_EmptyCut_MidRange)); + // short_str.replace_with_range(end, end, long_range) + assert(test(ShortString_End_EmptyCut_LongRange)); + } + + { // Long string. + // Replace at the beginning. + + // long_str.replace_with_range(begin, begin, empty_range) + assert(test(LongString_Begin_EmptyCut_EmptyRange)); + // long_str.replace_with_range(begin, begin, one_element_range) + assert(test(LongString_Begin_EmptyCut_OneElementRange)); + // long_str.replace_with_range(begin, begin, mid_range) + assert(test(LongString_Begin_EmptyCut_MidRange)); + // long_str.replace_with_range(begin, begin, long_range) + assert(test(LongString_Begin_EmptyCut_LongRange)); + // long_str.replace_with_range(begin, begin, longer_than_capacity_range) + assert(test(LongString_Begin_EmptyCut_LongerThanCapacityRange)); + // long_str.replace_with_range(begin, begin + 1, empty_range) + assert(test(LongString_Begin_OneElementCut_EmptyRange)); + // long_str.replace_with_range(begin, begin + 1, one_element_range) + assert(test(LongString_Begin_OneElementCut_OneElementRange)); + // long_str.replace_with_range(begin, begin + 1, mid_range) + assert(test(LongString_Begin_OneElementCut_MidRange)); + // long_str.replace_with_range(begin, begin + 1, long_range) + assert(test(LongString_Begin_OneElementCut_LongRange)); + // long_str.replace_with_range(begin, begin + 1, longer_than_capacity_range) + assert(test(LongString_Begin_OneElementCut_LongerThanCapacityRange)); + // long_str.replace_with_range(begin, begin + 3, empty_range) + assert(test(LongString_Begin_MidSizedCut_EmptyRange)); + // long_str.replace_with_range(begin, begin + 3, one_element_range) + assert(test(LongString_Begin_MidSizedCut_OneElementRange)); + // long_str.replace_with_range(begin, begin + 3, mid_range) + assert(test(LongString_Begin_MidSizedCut_MidRange)); + // long_str.replace_with_range(begin, begin + 3, long_range) + assert(test(LongString_Begin_MidSizedCut_LongRange)); + // long_str.replace_with_range(begin, begin + 3, longer_than_capacity_range) + assert(test(LongString_Begin_MidSizedCut_LongerThanCapacityRange)); + // long_str.replace_with_range(begin, end, empty_range) + assert(test(LongString_Begin_CutToEnd_EmptyRange)); + // long_str.replace_with_range(begin, end, one_element_range) + assert(test(LongString_Begin_CutToEnd_OneElementRange)); + // long_str.replace_with_range(begin, end, mid_range) + assert(test(LongString_Begin_CutToEnd_MidRange)); + // long_str.replace_with_range(begin, end, long_range) + assert(test(LongString_Begin_CutToEnd_LongRange)); + // long_str.replace_with_range(begin, end, longer_than_capacity_range) + assert(test(LongString_Begin_CutToEnd_LongerThanCapacityRange)); + + // Replace in the middle. + + // long_str.replace_with_range(mid, mid, empty_range) + assert(test(LongString_Mid_EmptyCut_EmptyRange)); + // long_str.replace_with_range(mid, mid, one_element_range) + assert(test(LongString_Mid_EmptyCut_OneElementRange)); + // long_str.replace_with_range(mid, mid, mid_range) + assert(test(LongString_Mid_EmptyCut_MidRange)); + // long_str.replace_with_range(mid, mid, long_range) + assert(test(LongString_Mid_EmptyCut_LongRange)); + // long_str.replace_with_range(mid, mid, longer_than_capacity_range) + assert(test(LongString_Mid_EmptyCut_LongerThanCapacityRange)); + // long_str.replace_with_range(mid, mid + 1, empty_range) + assert(test(LongString_Mid_OneElementCut_EmptyRange)); + // long_str.replace_with_range(mid, mid + 1, one_element_range) + assert(test(LongString_Mid_OneElementCut_OneElementRange)); + // long_str.replace_with_range(mid, mid + 1, mid_range) + assert(test(LongString_Mid_OneElementCut_MidRange)); + // long_str.replace_with_range(mid, mid + 1, long_range) + assert(test(LongString_Mid_OneElementCut_LongRange)); + // long_str.replace_with_range(mid, mid + 1, longer_than_capacity_range) + assert(test(LongString_Mid_OneElementCut_LongerThanCapacityRange)); + // long_str.replace_with_range(mid, mid + 3, empty_range) + assert(test(LongString_Mid_MidSizedCut_EmptyRange)); + // long_str.replace_with_range(mid, mid + 3, one_element_range) + assert(test(LongString_Mid_MidSizedCut_OneElementRange)); + // long_str.replace_with_range(mid, mid + 3, mid_range) + assert(test(LongString_Mid_MidSizedCut_MidRange)); + // long_str.replace_with_range(mid, mid + 3, long_range) + assert(test(LongString_Mid_MidSizedCut_LongRange)); + // long_str.replace_with_range(mid, mid + 3, longer_than_capacity_range) + assert(test(LongString_Mid_MidSizedCut_LongerThanCapacityRange)); + // long_str.replace_with_range(mid, end, empty_range) + assert(test(LongString_Mid_CutToEnd_EmptyRange)); + // long_str.replace_with_range(mid, end, one_element_range) + assert(test(LongString_Mid_CutToEnd_OneElementRange)); + // long_str.replace_with_range(mid, end, mid_range) + assert(test(LongString_Mid_CutToEnd_MidRange)); + // long_str.replace_with_range(mid, end, long_range) + assert(test(LongString_Mid_CutToEnd_LongRange)); + // long_str.replace_with_range(mid, end, longer_than_capacity_range) + assert(test(LongString_Mid_CutToEnd_LongerThanCapacityRange)); + + // Replace at the end. + + // long_str.replace_with_range(end, end, empty_range) + assert(test(LongString_End_EmptyCut_EmptyRange)); + // long_str.replace_with_range(end, end, one_element_range) + assert(test(LongString_End_EmptyCut_OneElementRange)); + // long_str.replace_with_range(end, end, mid_range) + assert(test(LongString_End_EmptyCut_MidRange)); + // long_str.replace_with_range(end, end, long_range) + assert(test(LongString_End_EmptyCut_LongRange)); + // long_str.replace_with_range(end, end, longer_than_capacity_range) + assert(test(LongString_End_EmptyCut_LongerThanCapacityRange)); + } +} + +void test_string_replace_with_range_exception_safety_throwing_allocator() { +#if !defined(TEST_HAS_NO_EXCEPTIONS) + // Note: the input string must be long enough to prevent SSO, otherwise the allocator won't be used. + std::string in(64, 'a'); + + try { + ThrowingAllocator alloc; + + globalMemCounter.reset(); + std::basic_string, ThrowingAllocator> c(alloc); + c.replace_with_range(c.end(), c.end(), in); + assert(false); // The function call above should throw. + + } catch (int) { + assert(globalMemCounter.new_called == globalMemCounter.delete_called); + } +#endif +} + +constexpr void test_string_replace_with_range_rvalue_range() { + // Make sure that the input range can be passed by both an lvalue and an rvalue reference and regardless of constness. + + { // Lvalue. + std::string in = "abc"; + std::string c = "123"; + c.replace_with_range(c.begin(), c.end(), in); + } + + { // Const lvalue. + const std::string in = "abc"; + std::string c = "123"; + c.replace_with_range(c.begin(), c.end(), in); + } + + { // Rvalue. + std::string in = "abc"; + std::string c = "123"; + c.replace_with_range(c.begin(), c.end(), std::move(in)); + } + + { // Const rvalue. + const std::string in = "abc"; + std::string c = "123"; + c.replace_with_range(c.begin(), c.end(), std::move(in)); + } +} + +constexpr bool test_constexpr() { + for_all_iterators_and_allocators_constexpr([]() { + test_string_replace_with_range(); + }); + test_string_replace_with_range_rvalue_range(); + + return true; +} + +// Tested cases: +// - different kinds of replacements (varying the size of the initial string, the cut point and the cut size, and the +// size of the input range); +// - an exception is thrown when allocating new elements. +int main(int, char**) { + static_assert(test_constraints_replace_with_range()); + + for_all_iterators_and_allocators_constexpr([]() { + test_string_replace_with_range(); + }); + test_string_replace_with_range_rvalue_range(); + static_assert(test_constexpr()); + + // Note: `test_string_replace_with_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot + // throw. + test_string_replace_with_range_exception_safety_throwing_allocator(); + + return 0; +}