template<class InputIterator>
basic_string(InputIterator begin, InputIterator end,
const allocator_type& a = allocator_type()); // constexpr since C++20
+ template<container-compatible-range<charT> R>
+ constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // since C++23
basic_string(initializer_list<value_type>, 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
basic_string& append(size_type n, value_type c); // constexpr since C++20
template<class InputIterator>
basic_string& append(InputIterator first, InputIterator last); // constexpr since C++20
+ template<container-compatible-range<charT> R>
+ constexpr basic_string& append_range(R&& rg); // C++23
basic_string& append(initializer_list<value_type>); // constexpr since C++20
void push_back(value_type c); // constexpr since C++20
basic_string& assign(size_type n, value_type c); // constexpr since C++20
template<class InputIterator>
basic_string& assign(InputIterator first, InputIterator last); // constexpr since C++20
+ template<container-compatible-range<charT> R>
+ constexpr basic_string& assign_range(R&& rg); // C++23
basic_string& assign(initializer_list<value_type>); // constexpr since C++20
basic_string& insert(size_type pos1, const basic_string& str); // constexpr since C++20
iterator insert(const_iterator p, size_type n, value_type c); // constexpr since C++20
template<class InputIterator>
iterator insert(const_iterator p, InputIterator first, InputIterator last); // constexpr since C++20
+ template<container-compatible-range<charT> R>
+ constexpr iterator insert_range(const_iterator p, R&& rg); // C++23
iterator insert(const_iterator p, initializer_list<value_type>); // constexpr since C++20
basic_string& erase(size_type pos = 0, size_type n = npos); // constexpr since C++20
basic_string& replace(const_iterator i1, const_iterator i2, size_type n, value_type c); // constexpr since C++20
template<class InputIterator>
basic_string& replace(const_iterator i1, const_iterator i2, InputIterator j1, InputIterator j2); // constexpr since C++20
+ template<container-compatible-range<charT> 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<value_type>); // constexpr since C++20
size_type copy(value_type* s, size_type n, size_type pos = 0) const; // constexpr since C++20
char_traits<typename iterator_traits<InputIterator>::value_type>,
Allocator>; // C++17
+template<ranges::input_range R,
+ class Allocator = allocator<ranges::range_value_t<R>>>
+ basic_string(from_range_t, R&&, Allocator = Allocator())
+ -> basic_string<ranges::range_value_t<R>, char_traits<ranges::range_value_t<R>>,
+ Allocator>; // C++23
+
+template<class charT,
+ class traits,
+ class Allocator = allocator<charT>>
+ explicit basic_string(basic_string_view<charT, traits>, const Allocator& = Allocator())
+ -> basic_string<charT, traits, Allocator>; // C++17
+
+template<class charT,
+ class traits,
+ class Allocator = allocator<charT>>
+ basic_string(basic_string_view<charT, traits>,
+ typename see below::size_type, typename see below::size_type,
+ const Allocator& = Allocator())
+ -> basic_string<charT, traits, Allocator>; // C++17
+
template<class charT, class traits, class Allocator>
basic_string<charT, traits, Allocator>
operator+(const basic_string<charT, traits, Allocator>& lhs,
#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>
#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>
> {};
struct __uninitialized_size_tag {};
+struct __init_with_sentinel_tag {};
template<class _CharT, class _Traits, class _Allocator>
class basic_string
}
}
+ template <class _Iter, class _Sent>
+ _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));
+ }
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 iterator __make_iterator(pointer __p) {
return iterator(__p);
}
__init(__first, __last);
}
+#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));
+ }
+ }
+#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()) {
_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<value_type> __il) {return append(__il.begin(), __il.size());}
_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<ranges::iterator_t<_Range>>::value &&
+ (ranges::forward_range<_Range> || ranges::sized_range<_Range>)) {
+ size_type __n = static_cast<size_type>(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<value_type> __il) {return assign(__il.begin(), __il.size());}
_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<size_type>(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) {
difference_type __p = __pos - begin();
_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<value_type> __il)
return !__libcpp_is_constant_evaluated() && (__sz < __min_cap);
}
- template <class _ForwardIterator>
+ template <class _Iterator, class _Sentinel>
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
+ void __assign_trivial(_Iterator __first, _Sentinel __last, size_type __n);
+
+ template <class _Iterator, class _Sentinel>
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
+ void __assign_with_sentinel(_Iterator __first, _Sentinel __last);
+
+ template <class _ForwardIterator, class _Sentinel>
_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;
return begin() + __ip;
}
+ template<class _Iterator, class _Sentinel>
+ _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(); }
template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void __init(_ForwardIterator __first, _ForwardIterator __last);
+ template <class _InputIterator, class _Sentinel>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+ void __init_with_sentinel(_InputIterator __first, _Sentinel __last);
+ template <class _InputIterator, class _Sentinel>
+ _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);
-> basic_string<_CharT, _Traits, _Allocator>;
#endif
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+ class _Allocator = allocator<ranges::range_value_t<_Range>>,
+ class = enable_if_t<__is_allocator<_Allocator>::value>
+ >
+basic_string(from_range_t, _Range&&, _Allocator = _Allocator())
+ -> basic_string<ranges::range_value_t<_Range>, char_traits<ranges::range_value_t<_Range>>, _Allocator>;
+#endif
+
template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX20
void basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s,
_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 <class _CharT, class _Traits, class _Allocator>
+template <class _InputIterator, class _Sentinel>
+_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
{
_LIBCPP_CONSTEXPR_SINCE_CXX20 void
basic_string<_CharT, _Traits, _Allocator>::__init(_ForwardIterator __first, _ForwardIterator __last)
{
+ size_type __sz = static_cast<size_type>(std::distance(__first, __last));
+ __init_with_size(__first, __last, __sz);
+}
+
+template <class _CharT, class _Traits, class _Allocator>
+template <class _InputIterator, class _Sentinel>
+_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<size_type>(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
{
_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 <class _CharT, class _Traits, class _Allocator>
+template <class _InputIterator, class _Sentinel>
+_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 <class _CharT, class _Traits, class _Allocator>
_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<size_type>(std::distance(__first, __last)) : 0;
+ if (__string_is_trivial_iterator<_ForwardIterator>::value) {
+ size_type __n = static_cast<size_type>(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);
- }
- else
- {
- const basic_string __temp(__first, __last, __alloc());
- assign(__temp.data(), __temp.size());
+ return *this;
+}
+
+template <class _CharT, class _Traits, class _Allocator>
+template <class _Iterator, class _Sentinel>
+_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_UNCATEGORIZED(
+ __string_is_trivial_iterator<_Iterator>::value, "The iterator type given to `__assign_trivial` must be trivial");
+
+ size_type __cap = capacity();
+ if (__cap < __n) {
+ // 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);
}
template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename basic_string<_CharT, _Traits, _Allocator>::iterator
basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last)
{
+ auto __n = static_cast<size_type>(std::distance(__first, __last));
+ return __insert_with_size(__pos, __first, __last, __n);
+}
+
+template <class _CharT, class _Traits, class _Allocator>
+template<class _Iterator, class _Sentinel>
+_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<size_type>(__pos - begin());
- size_type __n = static_cast<size_type>(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());
}
}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// 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<container-compatible-range<charT> R>
+// constexpr basic_string& replace_with_range(const_iterator i1, const_iterator i2, R&& rg); // C++23
+
+#include <string>
+#include <utility>
+
+#include "../../../../containers/sequences/insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+template <class Range>
+concept HasReplaceWithRange = requires (std::string& c, Range&& range) {
+ c.replace_with_range(c.end(), c.end(), std::forward<Range>(range));
+};
+
+constexpr bool test_constraints_replace_with_range() {
+ // Input range with the same value type.
+ static_assert(HasReplaceWithRange<InputRange<char>>);
+ // Input range with a convertible value type.
+ static_assert(HasReplaceWithRange<InputRange<unsigned char>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasReplaceWithRange<InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!HasReplaceWithRange<InputRangeNotDerivedFrom>);
+ static_assert(!HasReplaceWithRange<InputRangeNotIndirectlyReadable>);
+ static_assert(!HasReplaceWithRange<InputRangeNotInputOrOutputIterator>);
+
+ return true;
+}
+
+using StrBuffer = Buffer<char, 256>;
+
+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 <class Iter, class Sent, class Alloc>
+constexpr void test_string_replace_with_range() {
+ auto test = [&](const TestCaseReplacement& test_case) {
+ using Container = std::basic_string<char, std::char_traits<char>, 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<Iter, Sent>(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));
+ }
+}
+
+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<char, const char*>([]<class Iter, class Sent, class Alloc>() {
+ test_string_replace_with_range<Iter, Sent, Alloc>();
+ });
+ 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<char, const char*>([]<class Iter, class Sent, class Alloc>() {
+ test_string_replace_with_range<Iter, Sent, Alloc>();
+ });
+ 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.
+ {
+#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<char> alloc;
+
+ globalMemCounter.reset();
+ std::basic_string<char, std::char_traits<char>, ThrowingAllocator<char>> 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
+ }
+
+ return 0;
+}