// xstring internal header // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef _XSTRING_ #define _XSTRING_ #include #if _STL_COMPILER_PREPROCESSOR #include <__msvc_sanitizer_annotate_container.hpp> #include <__msvc_string_view.hpp> #include #include #if _HAS_CXX17 #include #endif // _HAS_CXX17 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new _STD_BEGIN template class _String_const_iterator : public _Iterator_base { public: #if _HAS_CXX20 using iterator_concept = contiguous_iterator_tag; #endif // _HAS_CXX20 using iterator_category = random_access_iterator_tag; using value_type = typename _Mystr::value_type; using difference_type = typename _Mystr::difference_type; using pointer = typename _Mystr::const_pointer; using reference = const value_type&; _CONSTEXPR20 _String_const_iterator() noexcept : _Ptr() {} _CONSTEXPR20 _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept : _Ptr(_Parg) { this->_Adopt(_Pstring); } _NODISCARD _CONSTEXPR20 reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot dereference value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Mycont, "cannot dereference string iterator because the iterator was " "invalidated (e.g. reallocation occurred, or the string was destroyed)"); const auto _Contptr = _Mycont->_Myptr(); const auto _Rawptr = _Unfancy(_Ptr); _STL_VERIFY(_Contptr <= _Rawptr && _Rawptr < _Contptr + _Mycont->_Mysize, "cannot dereference string iterator because it is out of range (e.g. an end iterator)"); #endif // _ITERATOR_DEBUG_LEVEL >= 1 _Analysis_assume_(_Ptr); return *_Ptr; } _NODISCARD _CONSTEXPR20 pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } _CONSTEXPR20 _String_const_iterator& operator++() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot increment value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Mycont, "cannot increment string iterator because the iterator was " "invalidated (e.g. reallocation occurred, or the string was destroyed)"); _STL_VERIFY(_Unfancy(_Ptr) < _Mycont->_Myptr() + _Mycont->_Mysize, "cannot increment string iterator past end"); #endif // _ITERATOR_DEBUG_LEVEL >= 1 ++_Ptr; return *this; } _CONSTEXPR20 _String_const_iterator operator++(int) noexcept { _String_const_iterator _Tmp = *this; ++*this; return _Tmp; } _CONSTEXPR20 _String_const_iterator& operator--() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot decrement value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Mycont, "cannot decrement string iterator because the iterator was " "invalidated (e.g. reallocation occurred, or the string was destroyed)"); _STL_VERIFY(_Mycont->_Myptr() < _Unfancy(_Ptr), "cannot decrement string iterator before begin"); #endif // _ITERATOR_DEBUG_LEVEL >= 1 --_Ptr; return *this; } _CONSTEXPR20 _String_const_iterator operator--(int) noexcept { _String_const_iterator _Tmp = *this; --*this; return _Tmp; } _CONSTEXPR20 void _Verify_offset(const difference_type _Off) const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 if (_Off == 0) { return; } _STL_VERIFY(_Ptr, "cannot seek value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); _STL_VERIFY(_Mycont, "cannot seek string iterator because the iterator was " "invalidated (e.g. reallocation occurred, or the string was destroyed)"); const auto _Contptr = _Mycont->_Myptr(); const auto _Rawptr = _Unfancy(_Ptr); if (_Off < 0) { _STL_VERIFY(_Contptr - _Rawptr <= _Off, "cannot seek string iterator before begin"); } if (_Off > 0) { using _Size_type = typename _Mystr::size_type; const auto _Left = _Mycont->_Mysize - static_cast<_Size_type>(_Rawptr - _Contptr); _STL_VERIFY(static_cast<_Size_type>(_Off) <= _Left, "cannot seek string iterator after end"); } #else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv (void) _Off; #endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ } _CONSTEXPR20 _String_const_iterator& operator+=(const difference_type _Off) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _Verify_offset(_Off); #endif // _ITERATOR_DEBUG_LEVEL >= 1 _Ptr += _Off; return *this; } _NODISCARD _CONSTEXPR20 _String_const_iterator operator+(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; _Tmp += _Off; return _Tmp; } _NODISCARD friend _CONSTEXPR20 _String_const_iterator operator+( const difference_type _Off, _String_const_iterator _Next) noexcept { _Next += _Off; return _Next; } _CONSTEXPR20 _String_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } _NODISCARD _CONSTEXPR20 _String_const_iterator operator-(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; _Tmp -= _Off; return _Tmp; } _NODISCARD _CONSTEXPR20 difference_type operator-(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return static_cast(_Ptr - _Right._Ptr); } _NODISCARD _CONSTEXPR20 reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } _NODISCARD _CONSTEXPR20 bool operator==(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr == _Right._Ptr; } #if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _STD _Unfancy_maybe_null(_Ptr) <=> _STD _Unfancy_maybe_null(_Right._Ptr); } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { return !(*this == _Right); } _NODISCARD bool operator<(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr < _Right._Ptr; } _NODISCARD bool operator>(const _String_const_iterator& _Right) const noexcept { return _Right < *this; } _NODISCARD bool operator<=(const _String_const_iterator& _Right) const noexcept { return !(_Right < *this); } _NODISCARD bool operator>=(const _String_const_iterator& _Right) const noexcept { return !(*this < _Right); } #endif // !_HAS_CXX20 _CONSTEXPR20 void _Compat(const _String_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(this->_Getcont() == _Right._Getcont(), "string iterators incompatible (e.g. point to different string instances)"); #else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 / _ITERATOR_DEBUG_LEVEL == 0 vvv (void) _Right; #endif // ^^^ _ITERATOR_DEBUG_LEVEL == 0 ^^^ } #if _ITERATOR_DEBUG_LEVEL >= 1 friend _CONSTEXPR20 void _Verify_range( const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { _STL_VERIFY(_First._Getcont() == _Last._Getcont(), "string iterators in range are from different containers"); _STL_VERIFY(_First._Ptr <= _Last._Ptr, "string iterator range transposed"); } #endif // _ITERATOR_DEBUG_LEVEL >= 1 using _Prevent_inheriting_unwrap = _String_const_iterator; _NODISCARD _CONSTEXPR20 const value_type* _Unwrapped() const noexcept { return _STD _Unfancy_maybe_null(_Ptr); } _CONSTEXPR20 void _Seek_to(const value_type* _It) noexcept { _Ptr = _STD _Refancy_maybe_null(const_cast(_It)); } pointer _Ptr; // pointer to element in string }; #if _HAS_CXX20 template struct pointer_traits<_String_const_iterator<_Mystr>> { using pointer = _String_const_iterator<_Mystr>; using element_type = const pointer::value_type; using difference_type = pointer::difference_type; _NODISCARD static constexpr element_type* to_address(const pointer _Iter) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 const auto _Mycont = static_cast(_Iter._Getcont()); if (!_Mycont) { _STL_VERIFY(!_Iter._Ptr, "cannot convert string iterator to pointer because the iterator was invalidated " "(e.g. reallocation occurred, or the string was destroyed)"); } #endif // _ITERATOR_DEBUG_LEVEL >= 1 const auto _Rawptr = _STD to_address(_Iter._Ptr); #if _ITERATOR_DEBUG_LEVEL >= 1 if (_Mycont) { const auto _Contptr = _Mycont->_Myptr(); _STL_VERIFY(_Contptr <= _Rawptr && _Rawptr <= _Contptr + _Mycont->_Mysize, "cannot convert string iterator to pointer because it is out of range"); } #endif // _ITERATOR_DEBUG_LEVEL >= 1 return _Rawptr; } }; #endif // _HAS_CXX20 template class _String_iterator : public _String_const_iterator<_Mystr> { public: using _Mybase = _String_const_iterator<_Mystr>; #if _HAS_CXX20 using iterator_concept = contiguous_iterator_tag; #endif // _HAS_CXX20 using iterator_category = random_access_iterator_tag; using value_type = typename _Mystr::value_type; using difference_type = typename _Mystr::difference_type; using pointer = typename _Mystr::pointer; using reference = value_type&; using _Mybase::_Mybase; _NODISCARD _CONSTEXPR20 reference operator*() const noexcept { return const_cast(_Mybase::operator*()); } _NODISCARD _CONSTEXPR20 pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } _CONSTEXPR20 _String_iterator& operator++() noexcept { _Mybase::operator++(); return *this; } _CONSTEXPR20 _String_iterator operator++(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator++(); return _Tmp; } _CONSTEXPR20 _String_iterator& operator--() noexcept { _Mybase::operator--(); return *this; } _CONSTEXPR20 _String_iterator operator--(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator--(); return _Tmp; } _CONSTEXPR20 _String_iterator& operator+=(const difference_type _Off) noexcept { _Mybase::operator+=(_Off); return *this; } _NODISCARD _CONSTEXPR20 _String_iterator operator+(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; _Tmp += _Off; return _Tmp; } _NODISCARD friend _CONSTEXPR20 _String_iterator operator+( const difference_type _Off, _String_iterator _Next) noexcept { _Next += _Off; return _Next; } _CONSTEXPR20 _String_iterator& operator-=(const difference_type _Off) noexcept { _Mybase::operator-=(_Off); return *this; } using _Mybase::operator-; _NODISCARD _CONSTEXPR20 _String_iterator operator-(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; _Tmp -= _Off; return _Tmp; } _NODISCARD _CONSTEXPR20 reference operator[](const difference_type _Off) const noexcept { return const_cast(_Mybase::operator[](_Off)); } using _Prevent_inheriting_unwrap = _String_iterator; _NODISCARD _CONSTEXPR20 value_type* _Unwrapped() const noexcept { return const_cast(_STD _Unfancy_maybe_null(this->_Ptr)); } }; #if _HAS_CXX20 template struct pointer_traits<_String_iterator<_Mystr>> { using pointer = _String_iterator<_Mystr>; using element_type = pointer::value_type; using difference_type = pointer::difference_type; _NODISCARD static constexpr element_type* to_address(const pointer _Iter) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 const auto _Mycont = static_cast(_Iter._Getcont()); if (!_Mycont) { _STL_VERIFY(!_Iter._Ptr, "cannot convert string iterator to pointer because the iterator was invalidated " "(e.g. reallocation occurred, or the string was destroyed)"); } #endif // _ITERATOR_DEBUG_LEVEL >= 1 const auto _Rawptr = _STD to_address(_Iter._Ptr); #if _ITERATOR_DEBUG_LEVEL >= 1 if (_Mycont) { const auto _Contptr = _Mycont->_Myptr(); _STL_VERIFY(_Contptr <= _Rawptr && _Rawptr <= _Contptr + _Mycont->_Mysize, "cannot convert string iterator to pointer because it is out of range"); } #endif // _ITERATOR_DEBUG_LEVEL >= 1 return const_cast(_Rawptr); } }; #endif // _HAS_CXX20 template struct _String_iter_types { using value_type = _Value_type; using size_type = _Size_type; using difference_type = _Difference_type; using pointer = _Pointer; using const_pointer = _Const_pointer; }; template class _String_val : public _Container_base { public: using value_type = typename _Val_types::value_type; using size_type = typename _Val_types::size_type; using difference_type = typename _Val_types::difference_type; using pointer = typename _Val_types::pointer; using const_pointer = typename _Val_types::const_pointer; using reference = value_type&; using const_reference = const value_type&; _CONSTEXPR20 _String_val() noexcept : _Bx() {} #if _MSVC_STL_DESTRUCTOR_TOMBSTONES _CONSTEXPR20 ~_String_val() noexcept { if constexpr (is_pointer_v) { if (!_STD _Is_constant_evaluated()) { const auto _Tombstone{reinterpret_cast(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)}; _Bx._Ptr = _Tombstone; _Mysize = 0; _Myres = (_Small_string_capacity + 1) | _Alloc_mask; // first capacity when entering large mode // The capacity indicates whether we're in small mode or large mode; see _Large_mode_engaged(). // The string would be usable in small mode, so we need large mode for the tombstone to be effective. // `_Small_string_capacity + 1` would be sufficient to make _Large_mode_engaged() return true. However, // basic_string uses a "roundup mask" when allocating; see _Calculate_growth(). So to avoid confusing // the SSO logic, we use the first capacity that would normally be used when entering large mode. } } } #endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES // length of internal buffer, [1, 16] (NB: used by the debugger visualizer) static constexpr size_type _BUF_SIZE = 16 / sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type); // roundup mask for allocated buffers, [0, 15] static constexpr size_type _Alloc_mask = sizeof(value_type) <= 1 ? 15 : sizeof(value_type) <= 2 ? 7 : sizeof(value_type) <= 4 ? 3 : sizeof(value_type) <= 8 ? 1 : 0; // capacity in small mode static constexpr size_type _Small_string_capacity = _BUF_SIZE - 1; _NODISCARD _CONSTEXPR20 value_type* _Myptr() noexcept { value_type* _Result = _Bx._Buf; if (_Large_mode_engaged()) { _Result = _Unfancy(_Bx._Ptr); } return _Result; } _NODISCARD _CONSTEXPR20 const value_type* _Myptr() const noexcept { const value_type* _Result = _Bx._Buf; if (_Large_mode_engaged()) { _Result = _Unfancy(_Bx._Ptr); } return _Result; } _NODISCARD _CONSTEXPR20 bool _Large_mode_engaged() const noexcept { return _Myres > _Small_string_capacity; } _CONSTEXPR20 void _Activate_SSO_buffer() noexcept { // start the lifetime of the array elements #if _HAS_CXX20 if (_STD is_constant_evaluated()) { for (size_type _Idx = 0; _Idx < _BUF_SIZE; ++_Idx) { _Bx._Buf[_Idx] = value_type(); } } #endif // _HAS_CXX20 } _CONSTEXPR20 void _Check_offset(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()] if (_Mysize < _Off) { _Xran(); } } _CONSTEXPR20 void _Check_offset_exclusive(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()) if (_Mysize <= _Off) { _Xran(); } } [[noreturn]] static void _Xran() { _Xout_of_range("invalid string position"); } _NODISCARD _CONSTEXPR20 size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { // trims _Size to the longest it can be assuming a string at/after _Off return (_STD min) (_Size, static_cast(_Mysize - _Off)); } union _Bxty { // storage for small buffer or pointer to larger one // This constructor previously initialized _Ptr. Don't rely on the new behavior without // renaming `_String_val` (and fixing the visualizer). _CONSTEXPR20 _Bxty() noexcept : _Buf() {} // user-provided, for fancy pointers _CONSTEXPR20 ~_Bxty() noexcept {} // user-provided, for fancy pointers value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr) _CONSTEXPR20 void _Switch_to_buf() noexcept { _STD _Destroy_in_place(_Ptr); #if _HAS_CXX20 // start the lifetime of the array elements if (_STD is_constant_evaluated()) { for (size_type _Idx = 0; _Idx < _BUF_SIZE; ++_Idx) { _Buf[_Idx] = value_type(); } } #endif // _HAS_CXX20 } }; _Bxty _Bx; // invariant: _Myres >= _Mysize, and _Myres >= _Small_string_capacity (after string's construction) // neither _Mysize nor _Myres takes account of the extra null terminator size_type _Mysize = 0; // current length of string (size) size_type _Myres = 0; // current storage reserved for string (capacity) }; // get _Ty's size after being EBCO'd template constexpr size_t _Size_after_ebco_v = is_empty_v<_Ty> ? 0 : sizeof(_Ty); struct _String_constructor_concat_tag { // tag to select constructors used by basic_string's concatenation operators (operator+) explicit _String_constructor_concat_tag() = default; }; template _CONSTEXPR20 void _Traits_copy_batch(_Out_writes_all_(_Count) _Traits_ch_t<_Traits>* const _First1, _In_reads_(_Count) const _UElem* const _First2, const size_t _Count) noexcept { _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_UElem, _Traits_ch_t<_Traits>, volatile _Traits_ch_t<_Traits>>); if constexpr (is_volatile_v<_UElem>) { for (size_t _Idx = 0; _Idx != _Count; ++_Idx) { _Traits::assign(_First1[_Idx], _Traits_ch_t<_Traits>{_First2[_Idx]}); } } else { (void) _Traits::copy(_First1, _First2, _Count); } } template _CONSTEXPR20 void _Traits_move_batch(_Out_writes_all_(_Count) _Traits_ch_t<_Traits>* const _First1, _In_reads_(_Count) const _UElem* const _First2, const size_t _Count) noexcept { _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_UElem, _Traits_ch_t<_Traits>, volatile _Traits_ch_t<_Traits>>); if constexpr (is_volatile_v<_UElem>) { bool _Loop_forward = true; for (const _UElem* _Src = _First2; _Src != _First2 + _Count; ++_Src) { if (_First1 == _Src) { _Loop_forward = false; break; } } if (_Loop_forward) { for (size_t _Idx = 0; _Idx != _Count; ++_Idx) { _Traits::assign(_First1[_Idx], _Traits_ch_t<_Traits>{_First2[_Idx]}); } } else { for (size_t _Idx = _Count; _Idx != 0; --_Idx) { _Traits::assign(_First1[_Idx - 1], _Traits_ch_t<_Traits>{_First2[_Idx - 1]}); } } } else { (void) _Traits::move(_First1, _First2, _Count); } } [[noreturn]] inline void _Xlen_string() { _Xlength_error("string too long"); } #if _HAS_CXX23 template concept _Contiguous_range_of = (_RANGES contiguous_range<_Rng>) && same_as>, _Ty>; #endif // _HAS_CXX23 #pragma warning(push) // Invalid annotation: 'NullTerminated' property may only be used on buffers whose elements are of integral or pointer // type #pragma warning(disable : 6510) _EXPORT_STD template , class _Alloc = allocator<_Elem>> class basic_string { // null-terminated transparent array of elements private: friend _Tidy_deallocate_guard; friend basic_stringbuf<_Elem, _Traits, _Alloc>; using _Alty = _Rebind_alloc_t<_Alloc, _Elem>; using _Alty_traits = allocator_traits<_Alty>; using _Scary_val = _String_val, _Simple_types<_Elem>, _String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type, typename _Alty_traits::pointer, typename _Alty_traits::const_pointer>>>; static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Elem, typename _Alloc::value_type>, _MISMATCHED_ALLOCATOR_MESSAGE("basic_string", "T")); static_assert(is_same_v<_Elem, typename _Traits::char_type>, "N4950 [string.require]/3 requires that the supplied " "char_traits character type match the string's character type."); static_assert(!is_array_v<_Elem> && is_trivially_copyable_v<_Elem> && is_trivially_default_constructible_v<_Elem> && is_standard_layout_v<_Elem>, "The character type of basic_string must be a non-array trivially copyable standard-layout type T where " "is_trivially_default_constructible_v is true. See N5001 [strings.general]/1."); public: using traits_type = _Traits; using allocator_type = _Alloc; using value_type = _Elem; using size_type = typename _Alty_traits::size_type; using difference_type = typename _Alty_traits::difference_type; using pointer = typename _Alty_traits::pointer; using const_pointer = typename _Alty_traits::const_pointer; using reference = value_type&; using const_reference = const value_type&; using iterator = _String_iterator<_Scary_val>; using const_iterator = _String_const_iterator<_Scary_val>; using reverse_iterator = _STD reverse_iterator; using const_reverse_iterator = _STD reverse_iterator; private: static constexpr size_type _BUF_SIZE = _Scary_val::_BUF_SIZE; static constexpr size_type _Alloc_mask = _Scary_val::_Alloc_mask; static constexpr size_type _Small_string_capacity = _Scary_val::_Small_string_capacity; // least real allocation size, including space reserved for terminating null static constexpr size_type _Least_allocation_size = _Small_string_capacity + 1 + 1; // When doing _String_val operations by memcpy, we are touching: // _String_val::_Bx::_Buf (type is array of _Elem) // _String_val::_Bx::_Ptr (type is pointer) // _String_val::_Mysize (type is size_type) // _String_val::_Myres (type is size_type) // N5001 [strings.general]/1 says _Elem must be trivially copyable standard-layout, so memcpy is safe. // We need to ask if pointer is safe to memcpy. // size_type must be an unsigned integral type so memcpy is safe. // We also need to disable memcpy if the user has supplied _Traits, since // they can observe traits::assign and similar. static constexpr bool _Can_memcpy_val = _Is_specialization_v<_Traits, char_traits> && is_trivially_copyable_v; // This offset skips over the _Container_base members, if any static constexpr size_t _Memcpy_val_offset = _Size_after_ebco_v<_Container_base>; static constexpr size_t _Memcpy_val_size = sizeof(_Scary_val) - _Memcpy_val_offset; template // TRANSITION, /clr:pure is incompatible with templated static constexpr data members // static constexpr bool _Is_elem_cvptr = // _Is_any_of_v, _Elem*, const _Elem*, volatile _Elem*, const volatile _Elem*>; using _Is_elem_cvptr = bool_constant< _Is_any_of_v, _Elem*, const _Elem*, volatile _Elem*, const volatile _Elem*>>; #if _HAS_CXX17 template using _Is_string_view_ish = enable_if_t>, negation>>, int>; #endif // _HAS_CXX17 #ifdef _INSERT_STRING_ANNOTATION _CONSTEXPR20 void _Create_annotation() const noexcept { // Annotates the valid range with shadow memory auto& _My_data = _Mypair._Myval2; _Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Myres, _My_data._Mysize); } _CONSTEXPR20 void _Remove_annotation() const noexcept { // Removes annotation of the range with shadow memory auto& _My_data = _Mypair._Myval2; _Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Mysize, _My_data._Myres); } _CONSTEXPR20 void _Modify_annotation(const size_type _Old_size, const size_type _New_size) const noexcept { if (_Old_size == _New_size) { return; } auto& _My_data = _Mypair._Myval2; _Apply_annotation(_My_data._Myptr(), _My_data._Myres, _Old_size, _New_size); } static _CONSTEXPR20 void _Apply_annotation(const value_type* const _First, const size_type _Capacity, const size_type _Old_size, const size_type _New_size) noexcept { if constexpr (!_Disable_ASan_container_annotations_for_allocator) { #if _HAS_CXX20 if (_STD is_constant_evaluated()) { return; } #endif // _HAS_CXX20 // Don't annotate small strings; only annotate on the heap. if (_Capacity <= _Small_string_capacity || !_Asan_string_should_annotate) { return; } // Note that `_Capacity`, `_Old_size`, and `_New_size` do not include the null terminator const void* const _End = _First + _Capacity + 1; const void* const _Old_last = _First + _Old_size + 1; const void* const _New_last = _First + _New_size + 1; constexpr bool _Large_string_always_asan_aligned = (_Container_allocation_minimum_asan_alignment) >= _Asan_granularity; // for the non-aligned buffer options, the buffer must always have size >= 9 bytes, // so it will always end at least one shadow memory section. _Asan_aligned_pointers _Aligned; if constexpr (_Large_string_always_asan_aligned) { _Aligned = {_First, _STD _Get_asan_aligned_after(_End)}; } else { _Aligned = _STD _Get_asan_aligned_first_end(_First, _End); } const void* const _Old_fixed = _Aligned._Clamp_to_end(_Old_last); const void* const _New_fixed = _Aligned._Clamp_to_end(_New_last); // --- always aligned case --- // old state: // [_First, _Old_last) valid // [_Old_last, asan_aligned_after(_End)) poison // new state: // [_First, _New_last) valid // [_New_last, asan_aligned_after(_End)) poison // --- sometimes non-aligned case --- // old state: // [_Aligned._First, _Old_fixed) valid // [_Old_fixed, _Aligned._End) poison // [_Aligned._End, _End) valid // new state: // [_Aligned._First, _New_fixed) valid // [_New_fixed, _Aligned._End) poison // [_Aligned._End, _End) valid _CSTD __sanitizer_annotate_contiguous_container(_Aligned._First, _Aligned._End, _Old_fixed, _New_fixed); } } #define _ASAN_STRING_REMOVE(_Str) (_Str)._Remove_annotation() #define _ASAN_STRING_CREATE(_Str) (_Str)._Create_annotation() #define _ASAN_STRING_MODIFY(_Str, _Old_size, _New_size) (_Str)._Modify_annotation(_Old_size, _New_size) #else // ^^^ defined(_INSERT_STRING_ANNOTATION) / !defined(_INSERT_STRING_ANNOTATION) vvv #define _ASAN_STRING_REMOVE(_Str) #define _ASAN_STRING_CREATE(_Str) #define _ASAN_STRING_MODIFY(_Str, _Old_size, _New_size) #endif // ^^^ !defined(_INSERT_STRING_ANNOTATION) ^^^ public: _CONSTEXPR20 basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { _Construct_empty(); } _CONSTEXPR20 explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct_empty(); } _CONSTEXPR20 basic_string(const basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { _Construct<_Construct_strategy::_From_string>(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } _CONSTEXPR20 basic_string(const basic_string& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct<_Construct_strategy::_From_string>(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } _CONSTEXPR20 basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, ) _Right._Mypair._Myval2._Check_offset(_Roff); _Construct<_Construct_strategy::_From_ptr>( _Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, npos)); } _CONSTEXPR20 basic_string( const basic_string& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Construct<_Construct_strategy::_From_ptr>( _Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count)); } #if _HAS_CXX23 constexpr basic_string(basic_string&& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, ), potentially move _Move_construct_from_substr(_Right, _Roff, npos); } constexpr basic_string( basic_string&& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count), potentially move _Move_construct_from_substr(_Right, _Roff, _Count); } #endif // _HAS_CXX23 _CONSTEXPR20 basic_string(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) : _Mypair(_Zero_then_variadic_args_t{}) { _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Count); } #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 _CONSTEXPR20 basic_string( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Count); } _CONSTEXPR20 basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Convert_size(_Traits::length(_Ptr))); } #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 _CONSTEXPR20 basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Convert_size(_Traits::length(_Ptr))); } #if _HAS_CXX23 basic_string(nullptr_t) = delete; #endif // _HAS_CXX23 _CONSTEXPR20 basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) : _Mypair(_Zero_then_variadic_args_t{}) { // construct from _Count * _Ch _Construct<_Construct_strategy::_From_char>(_Ch, _Count); } #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 _CONSTEXPR20 basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Count * _Ch with allocator _Construct<_Construct_strategy::_From_char>(_Ch, _Count); } template , int> = 0> _CONSTEXPR20 basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { _STD _Adl_verify_range(_First, _Last); auto _UFirst = _STD _Get_unwrapped(_First); auto _ULast = _STD _Get_unwrapped(_Last); if (_UFirst == _ULast) { _Construct_empty(); } else { if constexpr (_Is_elem_cvptr::value) { _Construct<_Construct_strategy::_From_ptr>( _UFirst, _STD _Convert_size(static_cast(_ULast - _UFirst))); } else if constexpr (_Is_cpp17_fwd_iter_v) { const auto _Length = static_cast(_STD distance(_UFirst, _ULast)); const auto _Count = _STD _Convert_size(_Length); _Construct_from_iter(_STD move(_UFirst), _STD move(_ULast), _Count); } else { _Construct_from_iter(_STD move(_UFirst), _STD move(_ULast)); } } } private: enum class _Allocation_policy { _At_least, _Exactly }; template <_Allocation_policy _Policy = _Allocation_policy::_At_least> _NODISCARD static _CONSTEXPR20 pointer _Allocate_for_capacity(_Alty& _Al, size_type& _Capacity) { _STL_INTERNAL_CHECK(_Capacity > _Small_string_capacity); ++_Capacity; // Take null terminator into consideration pointer _Fancy_ptr = nullptr; if constexpr (_Policy == _Allocation_policy::_At_least) { _Fancy_ptr = _Allocate_at_least_helper(_Al, _Capacity); } else { _STL_INTERNAL_STATIC_ASSERT(_Policy == _Allocation_policy::_Exactly); _Fancy_ptr = _Al.allocate(_Capacity); } #if _HAS_CXX20 // Start element lifetimes to avoid UB. This is a more general mechanism than _String_val::_Activate_SSO_buffer, // but likely more impactful to throughput. if (_STD is_constant_evaluated()) { _Elem* const _Ptr = _Unfancy(_Fancy_ptr); for (size_type _Idx = 0; _Idx < _Capacity; ++_Idx) { _STD construct_at(_Ptr + _Idx); } } #endif // _HAS_CXX20 --_Capacity; return _Fancy_ptr; } static _CONSTEXPR20 void _Deallocate_for_capacity( _Alty& _Al, const pointer _Old_ptr, const size_type _Capacity) noexcept { _STL_INTERNAL_CHECK(_Capacity > _Small_string_capacity); _Al.deallocate(_Old_ptr, static_cast(_Capacity + 1)); // +1 for null terminator } _CONSTEXPR20 void _Construct_empty() { auto& _My_data = _Mypair._Myval2; _My_data._Alloc_proxy(_STD _Get_proxy_allocator(_Getal())); // initialize basic_string data members _My_data._Mysize = 0; _My_data._Myres = _Small_string_capacity; _My_data._Activate_SSO_buffer(); // the _Traits::assign is last so the codegen doesn't think the char write can alias this _Traits::assign(_My_data._Bx._Buf[0], _Elem()); } enum class _Construct_strategy : uint8_t { _From_char, _From_ptr, _From_string }; template <_Construct_strategy _Strat, class _Char_or_ptr> _CONSTEXPR20 void _Construct(const _Char_or_ptr _Arg, _CRT_GUARDOVERFLOW const size_type _Count) { auto& _My_data = _Mypair._Myval2; _STL_INTERNAL_CHECK(!_My_data._Large_mode_engaged()); if constexpr (_Strat == _Construct_strategy::_From_char) { _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Char_or_ptr, _Elem>); } else { _STL_INTERNAL_STATIC_ASSERT(_Is_elem_cvptr<_Char_or_ptr>::value); } if (_Count > max_size()) { _Xlen_string(); // result too long } auto& _Al = _Getal(); auto _Alproxy = _STD _Get_proxy_allocator(_Al); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); if (_Count <= _Small_string_capacity) { _My_data._Mysize = _Count; _My_data._Myres = _Small_string_capacity; if constexpr (_Strat == _Construct_strategy::_From_char) { _Traits::assign(_My_data._Bx._Buf, static_cast(_Count), _Arg); _Traits::assign(_My_data._Bx._Buf[_Count], _Elem()); } else if constexpr (_Strat == _Construct_strategy::_From_ptr) { _STD _Traits_copy_batch<_Traits>(_My_data._Bx._Buf, _Arg, static_cast(_Count)); _Traits::assign(_My_data._Bx._Buf[_Count], _Elem()); } else { // _Strat == _Construct_strategy::_From_string #ifdef _INSERT_STRING_ANNOTATION _Traits::copy(_My_data._Bx._Buf, _Arg, static_cast(_Count + 1)); #else // ^^^ defined(_INSERT_STRING_ANNOTATION) / !defined(_INSERT_STRING_ANNOTATION) vvv _Traits::copy(_My_data._Bx._Buf, _Arg, _BUF_SIZE); #endif // ^^^ !defined(_INSERT_STRING_ANNOTATION) ^^^ } _Proxy._Release(); return; } size_type _New_capacity = _Calculate_growth(_Count, _Small_string_capacity, max_size()); const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); _My_data._Mysize = _Count; _My_data._Myres = _New_capacity; if constexpr (_Strat == _Construct_strategy::_From_char) { _Traits::assign(_Unfancy(_New_ptr), static_cast(_Count), _Arg); _Traits::assign(_Unfancy(_New_ptr)[_Count], _Elem()); } else if constexpr (_Strat == _Construct_strategy::_From_ptr) { _STD _Traits_copy_batch<_Traits>(_Unfancy(_New_ptr), _Arg, static_cast(_Count)); _Traits::assign(_Unfancy(_New_ptr)[_Count], _Elem()); } else { // _Strat == _Construct_strategy::_From_string _Traits::copy(_Unfancy(_New_ptr), _Arg, static_cast(_Count + 1)); } _ASAN_STRING_CREATE(*this); _Proxy._Release(); } template _CONSTEXPR20 void _Construct_from_iter(_Iter _First, const _Sent _Last, _Size _Count = {}) { // Pre: _Iter models input_iterator or meets the Cpp17InputIterator requirements. // Pre: [_First, _Last) is a valid range. // Pre: if _Iter models forward_iterator or meets the Cpp17ForwardIterator requirements, // then is_same_v<_Size, size_type> holds. // Pre: if is_same_v<_Size, size_type>, _Count is the length of [_First, _Last). // Pre: *this is in small mode; the lifetime of the SSO elements has already begun. auto& _My_data = _Mypair._Myval2; auto& _Al = _Getal(); auto _Alproxy = _STD _Get_proxy_allocator(_Al); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); _My_data._Mysize = 0; _My_data._Myres = _Small_string_capacity; if constexpr (is_same_v<_Size, size_type>) { if (_Count > max_size()) { _Xlen_string(); // result too long } if (_Count > _Small_string_capacity) { size_type _New_capacity = _Calculate_growth(_Count); const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); _My_data._Myres = _New_capacity; } } _Tidy_deallocate_guard _Guard{this}; constexpr bool _Can_construct_by_copy = _Is_specialization_v<_Traits, char_traits> && _Is_EcharT<_Elem> && is_same_v<_Size, size_type>; if constexpr (_Can_construct_by_copy) { const auto _Data = _My_data._Myptr(); _STD _Copy_n_unchecked4(_STD move(_First), _Count, _Data); _My_data._Mysize = _Count; _Data[_Count] = _Elem(); } else { for (; _First != _Last; ++_First) { if constexpr (!is_same_v<_Size, size_type>) { if (_My_data._Mysize == _My_data._Myres) { // Need to grow if (_My_data._Mysize == max_size()) { _Xlen_string(); // result too long } _Elem* const _Old_ptr = _My_data._Myptr(); size_type _New_capacity = _Calculate_growth(_My_data._Mysize + 1); const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws _Traits::copy(_Unfancy(_New_ptr), _Old_ptr, _My_data._Mysize); if (_My_data._Large_mode_engaged()) { // Need to deallocate old storage _Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres); _My_data._Bx._Ptr = _New_ptr; } else { _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); } _My_data._Myres = _New_capacity; } } _Elem* const _Ptr = _My_data._Myptr(); _Traits::assign(_Ptr[_My_data._Mysize], *_First); ++_My_data._Mysize; } _Elem* const _Ptr = _My_data._Myptr(); _Traits::assign(_Ptr[_My_data._Mysize], _Elem()); } _ASAN_STRING_CREATE(*this); _Guard._Target = nullptr; _Proxy._Release(); } public: #if _HAS_CXX23 template <_Container_compatible_range<_Elem> _Rng> constexpr basic_string(from_range_t, _Rng&& _Range, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { if constexpr (_RANGES sized_range<_Rng> || _RANGES forward_range<_Rng>) { const auto _Length = _To_unsigned_like(_RANGES distance(_Range)); const auto _Count = _Convert_size(_Length); if constexpr (_Contiguous_range_of<_Rng, _Elem>) { _Construct<_Construct_strategy::_From_ptr>(_RANGES data(_Range), _Count); } else { _Construct_from_iter(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range), _Count); } } else { _Construct_from_iter(_RANGES _Ubegin(_Range), _RANGES _Uend(_Range)); } } #endif // _HAS_CXX23 _CONSTEXPR20 basic_string(basic_string&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { _Mypair._Myval2._Alloc_proxy(_STD _Get_proxy_allocator(_Getal())); _Take_contents(_Right); } _CONSTEXPR20 basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept(_Alty_traits::is_always_equal::value) // strengthened : _Mypair(_One_then_variadic_args_t{}, _Al) { if constexpr (!_Alty_traits::is_always_equal::value) { if (_Getal() != _Right._Getal()) { _Construct<_Construct_strategy::_From_string>( _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); return; } } _Mypair._Myval2._Alloc_proxy(_STD _Get_proxy_allocator(_Getal())); _Take_contents(_Right); } _CONSTEXPR20 basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, const _Elem* const _Left_ptr, const size_type _Left_size, const _Elem* const _Right_ptr, const size_type _Right_size) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Source_of_al._Getal())) { _STL_INTERNAL_CHECK(_Left_size <= max_size()); _STL_INTERNAL_CHECK(_Right_size <= max_size()); _STL_INTERNAL_CHECK(_Right_size <= max_size() - _Left_size); const auto _New_size = static_cast(_Left_size + _Right_size); size_type _New_capacity = _Small_string_capacity; auto& _My_data = _Mypair._Myval2; _Elem* _Ptr = _My_data._Bx._Buf; auto _Alproxy = _STD _Get_proxy_allocator(_Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws if (_New_capacity < _New_size) { _New_capacity = _Calculate_growth(_New_size, _Small_string_capacity, max_size()); const pointer _Fancyptr = _Allocate_for_capacity(_Getal(), _New_capacity); // throws _Ptr = _Unfancy(_Fancyptr); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); } _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; _Traits::copy(_Ptr, _Left_ptr, static_cast(_Left_size)); _Traits::copy(_Ptr + static_cast(_Left_size), _Right_ptr, static_cast(_Right_size)); _Traits::assign(_Ptr[_New_size], _Elem()); _ASAN_STRING_CREATE(*this); _Proxy._Release(); } _CONSTEXPR20 basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Left._Getal()) { auto& _My_data = _Mypair._Myval2; auto& _Left_data = _Left._Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; _Left_data._Orphan_all(); _Right_data._Orphan_all(); const auto _Left_size = _Left_data._Mysize; const auto _Right_size = _Right_data._Mysize; const auto _Left_capacity = _Left_data._Myres; const auto _Right_capacity = _Right_data._Myres; // overflow is OK due to max_size() checks: const auto _New_size = static_cast(_Left_size + _Right_size); const bool _Fits_in_left = _Right_size <= _Left_capacity - _Left_size; if (_Fits_in_left && _Right_capacity <= _Left_capacity) { // take _Left's buffer, max_size() is OK because _Fits_in_left _My_data._Alloc_proxy(_STD _Get_proxy_allocator(_Getal())); // throws, hereafter nothrow in this block _Take_contents(_Left); const auto _Ptr = _My_data._Myptr(); _ASAN_STRING_MODIFY(*this, _Left_size, _New_size); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), static_cast(_Right_size + 1)); _My_data._Mysize = _New_size; return; } const bool _Fits_in_right = _Left_size <= _Right_capacity - _Right_size; if (_Allocators_equal(_Getal(), _Right._Getal()) && _Fits_in_right) { // take _Right's buffer, max_size() is OK because _Fits_in_right // At this point, we have tested: // !(_Fits_in_left && _Right_capacity <= _Left_capacity) && _Fits_in_right // therefore: (by De Morgan's Laws) // (!_Fits_in_left || _Right_capacity > _Left_capacity) && _Fits_in_right // therefore: (by the distributive property) // (!_Fits_in_left && _Fits_in_right) // implying _Right has more capacity // || (_Right_capacity > _Left_capacity && _Fits_in_right) // tests that _Right has more capacity // therefore: _Right must have more than the minimum capacity, so it must be _Large_mode_engaged() _STL_INTERNAL_CHECK(_Right_data._Large_mode_engaged()); _My_data._Alloc_proxy(_STD _Get_proxy_allocator(_Getal())); // throws, hereafter nothrow in this block _Take_contents(_Right); const auto _Ptr = _Unfancy(_My_data._Bx._Ptr); _ASAN_STRING_MODIFY(*this, _Right_size, _New_size); _Traits::move(_Ptr + _Left_size, _Ptr, static_cast(_Right_size + 1)); _Traits::copy(_Ptr, _Left_data._Myptr(), static_cast(_Left_size)); _My_data._Mysize = _New_size; return; } // can't use either buffer, reallocate const auto _Max = max_size(); if (_Max - _Left_size < _Right_size) { // check if max_size() is OK _Xlen_string(); } size_type _New_capacity = _Calculate_growth(_New_size, _Small_string_capacity, _Max); auto _Alproxy = _STD _Get_proxy_allocator(_Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws const pointer _Fancyptr = _Allocate_for_capacity(_Getal(), _New_capacity); // throws // nothrow hereafter _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; const auto _Ptr = _Unfancy(_Fancyptr); _Traits::copy(_Ptr, _Left_data._Myptr(), static_cast(_Left_size)); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), static_cast(_Right_size + 1)); _ASAN_STRING_CREATE(*this); _Proxy._Release(); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { const basic_string_view<_Elem, _Traits> _As_view = _Right; _Construct<_Construct_strategy::_From_ptr>(_As_view.data(), _Convert_size(_As_view.size())); } template >, int> = 0> _CONSTEXPR20 basic_string( const _Ty& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) using _Al const basic_string_view<_Elem, _Traits> _As_view = _Right; const auto _As_sub_view = _As_view.substr(static_cast(_Roff), static_cast(_Count)); _Construct<_Construct_strategy::_From_ptr>(_As_sub_view.data(), _Convert_size(_As_sub_view.size())); } #endif // _HAS_CXX17 #if _HAS_CXX20 _NODISCARD bool _Move_assign_from_buffer( _Elem* const _Right, const size_type _Size, const size_type _Actual_allocation_size) { // Move assign from a buffer, used exclusively by basic_stringbuf; returns _Large_mode_engaged() auto& _My_data = _Mypair._Myval2; _STL_INTERNAL_CHECK(!_My_data._Large_mode_engaged() && _My_data._Mysize == 0); _STL_INTERNAL_CHECK(_Size < _Actual_allocation_size); // So there is room for null terminator _Traits::assign(_Right[_Size], _Elem()); const bool _Is_large = _Actual_allocation_size >= _Least_allocation_size; if (_Is_large) { _ASAN_STRING_REMOVE(*this); _Construct_in_place(_My_data._Bx._Ptr, _Refancy(_Right)); _My_data._Mysize = _Size; _My_data._Myres = _Actual_allocation_size - 1; _ASAN_STRING_CREATE(*this); } else { _Traits::copy(_My_data._Bx._Buf, _Right, _Actual_allocation_size); _My_data._Mysize = _Size; _My_data._Myres = _Small_string_capacity; } return _Is_large; } // No instance of this type can exist where an exception may be thrown. struct _Released_buffer { pointer _Ptr; size_type _Size; size_type _Actual_allocation_size; }; _NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) { // Release to a buffer, or allocate a new one if in small string mode; used exclusively by basic_stringbuf _Released_buffer _Result; auto& _My_data = _Mypair._Myval2; _Result._Size = _My_data._Mysize; _My_data._Orphan_all(); _ASAN_STRING_REMOVE(*this); if (_My_data._Large_mode_engaged()) { _Result._Ptr = _My_data._Bx._Ptr; _Result._Actual_allocation_size = _My_data._Myres + 1; _My_data._Bx._Switch_to_buf(); } else { // use _Least_allocation_size to avoid small mode, if the buffer is assigned back size_type _Allocated = _Least_allocation_size; _Result._Ptr = _Allocate_at_least_helper(_Al, _Allocated); _Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE); _Result._Actual_allocation_size = _Allocated; } _My_data._Mysize = 0; _My_data._Myres = _Small_string_capacity; _Traits::assign(_My_data._Bx._Buf[0], _Elem()); return _Result; } #endif // _HAS_CXX20 _CONSTEXPR20 basic_string& operator=(basic_string&& _Right) noexcept(_Choose_pocma_v<_Alty> != _Pocma_values::_No_propagate_allocators) { if (this == _STD addressof(_Right)) { return *this; } auto& _Al = _Getal(); auto& _Right_al = _Right._Getal(); constexpr auto _Pocma_val = _Choose_pocma_v<_Alty>; if constexpr (_Pocma_val == _Pocma_values::_Propagate_allocators) { if (_Al != _Right_al) { // intentionally slams into noexcept on OOM, TRANSITION, VSO-466800 _Mypair._Myval2._Orphan_all(); _Mypair._Myval2._Reload_proxy(_STD _Get_proxy_allocator(_Al), _STD _Get_proxy_allocator(_Right_al)); } } else if constexpr (_Pocma_val == _Pocma_values::_No_propagate_allocators) { if (_Al != _Right_al) { assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); return *this; } } _Tidy_deallocate(); _Pocma(_Al, _Right_al); _Take_contents(_Right); return *this; } _CONSTEXPR20 basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { *this = _STD move(_Right); return *this; } private: _CONSTEXPR20 void _Take_contents(basic_string& _Right) noexcept { // assign by stealing _Right's buffer // pre: this != &_Right // pre: allocator propagation (POCMA) from _Right, if necessary, is complete // pre: *this owns no memory, iterators orphaned // (note: _Buf/_Ptr/_Mysize/_Myres may be garbage init) auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; #if !defined(_INSERT_STRING_ANNOTATION) if constexpr (_Can_memcpy_val) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { #if _ITERATOR_DEBUG_LEVEL != 0 if (_Right_data._Large_mode_engaged()) { // take ownership of _Right's iterators along with its buffer _Swap_proxy_and_iterators(_Right); } else { _Right_data._Orphan_all(); } #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _My_data_mem = reinterpret_cast(_STD addressof(_Mypair._Myval2)) + _Memcpy_val_offset; const auto _Right_data_mem = reinterpret_cast(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset; _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); _Right_data._Mysize = 0; _Right_data._Myres = _Small_string_capacity; _Right_data._Activate_SSO_buffer(); _Traits::assign(_Right_data._Bx._Buf[0], _Elem()); return; } } #endif // ^^^ !defined(_INSERT_STRING_ANNOTATION) ^^^ if (_Right_data._Large_mode_engaged()) { // steal buffer _Swap_proxy_and_iterators(_Right); _Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); _Right_data._Bx._Switch_to_buf(); } else { // copy small string buffer _Right_data._Orphan_all(); _My_data._Activate_SSO_buffer(); _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, static_cast(_Right_data._Mysize + 1)); } _My_data._Myres = _Right_data._Myres; _My_data._Mysize = _Right_data._Mysize; _Right_data._Mysize = 0; _Right_data._Myres = _Small_string_capacity; _Traits::assign(_Right_data._Bx._Buf[0], _Elem()); } #if _HAS_CXX23 constexpr void _Move_construct_from_substr(basic_string& _Right, const size_type _Roff, const size_type _Size_max) { auto& _Right_data = _Right._Mypair._Myval2; _Right_data._Check_offset(_Roff); const auto _Result_size = _Right_data._Clamp_suffix_size(_Roff, _Size_max); const auto _Right_ptr = _Right_data._Myptr(); auto& _Al = _Getal(); if (_Allocators_equal(_Al, _Right._Getal()) && _Result_size > _Small_string_capacity) { _Mypair._Myval2._Alloc_proxy(_STD _Get_proxy_allocator(_Al)); if (_Roff != 0) { _Traits::move(_Right_ptr, _Right_ptr + _Roff, static_cast(_Result_size)); } _Right._Eos(_Result_size); _Take_contents(_Right); } else { _Construct<_Construct_strategy::_From_ptr>(_Right_ptr + _Roff, _Result_size); } } #endif // _HAS_CXX23 public: _CONSTEXPR20 basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) : _Mypair(_One_then_variadic_args_t{}, _Al) { _Construct<_Construct_strategy::_From_ptr>(_Ilist.begin(), _Convert_size(_Ilist.size())); } _CONSTEXPR20 basic_string& operator=(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } _CONSTEXPR20 basic_string& operator+=(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } _CONSTEXPR20 basic_string& assign(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } _CONSTEXPR20 basic_string& append(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } _CONSTEXPR20 iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr()); insert(_Off, _Ilist.begin(), _Convert_size(_Ilist.size())); return begin() + static_cast(_Off); } _CONSTEXPR20 basic_string& replace( const const_iterator _First, const const_iterator _Last, const initializer_list<_Elem> _Ilist) { // replace with initializer_list _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Offset = static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()); const auto _Length = static_cast(_Last._Ptr - _First._Ptr); return replace(_Offset, _Length, _Ilist.begin(), _Convert_size(_Ilist.size())); } _CONSTEXPR20 ~basic_string() noexcept { _Tidy_deallocate(); #if _ITERATOR_DEBUG_LEVEL != 0 auto _Alproxy = _STD _Get_proxy_allocator(_Getal()); const auto _To_delete = _Mypair._Myval2._Myproxy; _Mypair._Myval2._Myproxy = nullptr; _Delete_plain_internal(_Alproxy, _To_delete); #endif // _ITERATOR_DEBUG_LEVEL != 0 } static constexpr auto npos{static_cast(-1)}; _CONSTEXPR20 basic_string& operator=(const basic_string& _Right) { if (this == _STD addressof(_Right)) { return *this; } auto& _Al = _Getal(); const auto& _Right_al = _Right._Getal(); if constexpr (_Choose_pocca_v<_Alty>) { if (_Al != _Right_al) { auto _Alproxy = _STD _Get_proxy_allocator(_Al); auto _Right_alproxy = _STD _Get_proxy_allocator(_Right_al); _Container_proxy_ptr<_Alty> _New_proxy(_Right_alproxy, _Leave_proxy_unbound{}); // throws const size_type _Right_size = _Right._Mypair._Myval2._Mysize; const _Elem* const _Right_ptr = _Right._Mypair._Myval2._Myptr(); if (_Right_size > _Small_string_capacity) { size_type _New_capacity = _Calculate_growth(_Right_size, _Small_string_capacity, _Right.max_size()); auto _Right_al_non_const = _Right_al; const pointer _New_ptr = _Allocate_for_capacity(_Right_al_non_const, _New_capacity); // throws _Traits::copy(_Unfancy(_New_ptr), _Right_ptr, _Right_size + 1); _Tidy_deallocate(); _Construct_in_place(_Mypair._Myval2._Bx._Ptr, _New_ptr); _Mypair._Myval2._Mysize = _Right_size; _Mypair._Myval2._Myres = _New_capacity; _ASAN_STRING_CREATE(*this); } else { _Tidy_deallocate(); _Traits::copy(_Mypair._Myval2._Bx._Buf, _Right_ptr, _Right_size + 1); _Mypair._Myval2._Mysize = _Right_size; _Mypair._Myval2._Myres = _Small_string_capacity; } _Pocca(_Al, _Right_al); _New_proxy._Bind(_Alproxy, _STD addressof(_Mypair._Myval2)); return *this; } } _Pocca(_Al, _Right_al); assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); return *this; } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& operator=(const _StringViewIsh& _Right) { return assign(_Right); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& operator=(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr); } #if _HAS_CXX23 basic_string& operator=(nullptr_t) = delete; #endif // _HAS_CXX23 _CONSTEXPR20 basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, 1); _Mypair._Myval2._Mysize = 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[0], _Ch); _Traits::assign(_Ptr[1], _Elem()); return *this; } _CONSTEXPR20 basic_string& operator+=(const basic_string& _Right) { return append(_Right); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& operator+=(const _StringViewIsh& _Right) { return append(_Right); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr); } _CONSTEXPR20 basic_string& operator+=(_Elem _Ch) { push_back(_Ch); return *this; } _CONSTEXPR20 basic_string& append(const basic_string& _Right) { return _Append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } _CONSTEXPR20 basic_string& append(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); return _Append(_Right._Mypair._Myval2._Myptr() + _Roff, _Count); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& append(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return _Append(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> _CONSTEXPR20 basic_string& append( const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.substr(static_cast(_Roff), static_cast(_Count))); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& append( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // append [_Ptr, _Ptr + _Count) return _Append(_Ptr, _Count); } _CONSTEXPR20 basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return _Append(_Ptr, _Convert_size(_Traits::length(_Ptr))); } _CONSTEXPR20 basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // append _Count * _Ch const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { const auto _New_size = static_cast(_Old_size + _Count); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Old_ptr + _Old_size, static_cast(_Count), _Ch); _Traits::assign(_Old_ptr[_Old_size + _Count], _Elem()); return *this; } return _Reallocate_grow_by( _Count, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Count, const _Elem _Ch) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Old_size)); _Traits::assign(_New_ptr + _Old_size, static_cast(_Count), _Ch); _Traits::assign(_New_ptr[_Old_size + _Count], _Elem()); }, _Count, _Ch); } template , int> = 0> _CONSTEXPR20 basic_string& append(const _Iter _First, const _Iter _Last) { // append [_First, _Last), input iterators _STD _Adl_verify_range(_First, _Last); const auto _UFirst = _STD _Get_unwrapped(_First); const auto _ULast = _STD _Get_unwrapped(_Last); if constexpr (_Is_elem_cvptr::value) { return _Append(_UFirst, _STD _Convert_size(static_cast(_ULast - _UFirst))); } else { const basic_string _Right(_UFirst, _ULast, get_allocator()); return _Append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } } #if _HAS_CXX23 template <_Container_compatible_range<_Elem> _Rng> constexpr basic_string& append_range(_Rng&& _Range) { if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) { const auto _Count = _Convert_size(_To_unsigned_like(_RANGES size(_Range))); return _Append(_RANGES data(_Range), _Count); } else { const basic_string _Right(from_range, _Range, get_allocator()); return _Append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } } #endif // _HAS_CXX23 _CONSTEXPR20 basic_string& assign(const basic_string& _Right) { *this = _Right; return *this; } _CONSTEXPR20 basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); return _Assign(_Right._Mypair._Myval2._Myptr() + _Roff, _Count); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& assign(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return _Assign(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> _CONSTEXPR20 basic_string& assign( const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.substr(static_cast(_Roff), static_cast(_Count))); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& assign( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) return _Assign(_Ptr, _Count); } _CONSTEXPR20 basic_string& assign(_In_z_ const _Elem* const _Ptr) { return _Assign(_Ptr, _Convert_size(_Traits::length(_Ptr))); } _CONSTEXPR20 basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // assign _Count * _Ch if (_Count <= _Mypair._Myval2._Myres) { _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _Count); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; _Traits::assign(_Old_ptr, _Count, _Ch); _Traits::assign(_Old_ptr[_Count], _Elem()); return *this; } return _Reallocate_for( _Count, [](_Elem* const _New_ptr, const size_type _Count, const _Elem _Ch) static { _Traits::assign(_New_ptr, _Count, _Ch); _Traits::assign(_New_ptr[_Count], _Elem()); }, _Ch); } template , int> = 0> _CONSTEXPR20 basic_string& assign(const _Iter _First, const _Iter _Last) { _STD _Adl_verify_range(_First, _Last); const auto _UFirst = _STD _Get_unwrapped(_First); const auto _ULast = _STD _Get_unwrapped(_Last); if constexpr (_Is_elem_cvptr::value) { return _Assign(_UFirst, _STD _Convert_size(static_cast(_ULast - _UFirst))); } else { basic_string _Right(_UFirst, _ULast, get_allocator()); if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) { _Mypair._Myval2._Orphan_all(); _Swap_data(_Right); return *this; } else { return _Assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } } } #if _HAS_CXX23 template <_Container_compatible_range<_Elem> _Rng> constexpr basic_string& assign_range(_Rng&& _Range) { if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) { const auto _Count = _Convert_size(_To_unsigned_like(_RANGES size(_Range))); return _Assign(_RANGES data(_Range), _Count); } else { basic_string _Right(from_range, _Range, get_allocator()); if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) { _Mypair._Myval2._Orphan_all(); _Swap_data(_Right); return *this; } else { return _Assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } } } #endif // _HAS_CXX23 _CONSTEXPR20 basic_string& insert(const size_type _Off, const basic_string& _Right) { // insert _Right at _Off return _Insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } _CONSTEXPR20 basic_string& insert( const size_type _Off, const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); return _Insert(_Off, _Right._Mypair._Myval2._Myptr() + _Roff, _Count); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { // insert _Right at _Off const basic_string_view<_Elem, _Traits> _As_view = _Right; return _Insert(_Off, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> _CONSTEXPR20 basic_string& insert( const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.substr(static_cast(_Roff), static_cast(_Count))); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& insert( const size_type _Off, _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // insert [_Ptr, _Ptr + _Count) at _Off return _Insert(_Off, _Ptr, _Count); } _CONSTEXPR20 basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { // insert [_Ptr, ) at _Off return _Insert(_Off, _Ptr, _Convert_size(_Traits::length(_Ptr))); } _CONSTEXPR20 basic_string& insert( const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Ch at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { const auto _New_size = static_cast(_Old_size + _Count); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; _Traits::move( _Insert_at + _Count, _Insert_at, static_cast(_Old_size - _Off + 1)); // move suffix + null down _Traits::assign(_Insert_at, static_cast(_Count), _Ch); // fill hole return *this; } return _Reallocate_grow_by( _Count, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off, const size_type _Count, const _Elem _Ch) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Off)); _Traits::assign(_New_ptr + _Off, static_cast(_Count), _Ch); _Traits::copy(_New_ptr + _Off + _Count, _Old_ptr + _Off, static_cast(_Old_size - _Off + 1)); }, _Off, _Count, _Ch); } _CONSTEXPR20 iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr()); insert(_Off, 1, _Ch); return begin() + static_cast(_Off); } _CONSTEXPR20 iterator insert( const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Elem at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr()); insert(_Off, _Count, _Ch); return begin() + static_cast(_Off); } template , int> = 0> _CONSTEXPR20 iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { // insert [_First, _Last) at _Where, input iterators #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr()); _STD _Adl_verify_range(_First, _Last); const auto _UFirst = _STD _Get_unwrapped(_First); const auto _ULast = _STD _Get_unwrapped(_Last); if constexpr (_Is_elem_cvptr::value) { _Insert(_Off, _UFirst, _STD _Convert_size(static_cast(_ULast - _UFirst))); } else { const basic_string _Right(_UFirst, _ULast, get_allocator()); _Insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } return begin() + static_cast(_Off); } #if _HAS_CXX23 template <_Container_compatible_range<_Elem> _Rng> constexpr iterator insert_range(const const_iterator _Where, _Rng&& _Range) { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr()); if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) { const auto _Count = _Convert_size(_To_unsigned_like(_RANGES size(_Range))); _Insert(_Off, _RANGES data(_Range), _Count); } else { const basic_string _Right(from_range, _Range, get_allocator()); _Insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } return begin() + static_cast(_Off); } #endif // _HAS_CXX23 _CONSTEXPR20 basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) _Mypair._Myval2._Check_offset(_Off); _Eos(_Off); return *this; } private: _CONSTEXPR20 basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); const size_type _Old_size = _Mypair._Myval2._Mysize; _Elem* const _My_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Erase_at = _My_ptr + _Off; const auto _New_size = static_cast(_Old_size - _Count); _Traits::move( _Erase_at, _Erase_at + _Count, static_cast(_New_size - _Off + 1)); // move suffix + null up _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; return *this; } public: _CONSTEXPR20 basic_string& erase(const size_type _Off, const size_type _Count) { // erase elements [_Off, _Off + _Count) _Mypair._Myval2._Check_offset(_Off); return _Erase_noexcept(_Off, _Count); } _CONSTEXPR20 iterator erase(const const_iterator _Where) noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_Where._Ptr) - _Mypair._Myval2._Myptr()); _Erase_noexcept(_Off, 1); return begin() + static_cast(_Off); } _CONSTEXPR20 iterator erase(const const_iterator _First, const const_iterator _Last) noexcept /* strengthened */ { _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()); _Erase_noexcept(_Off, static_cast(_Last._Ptr - _First._Ptr)); return begin() + static_cast(_Off); } _CONSTEXPR20 void clear() noexcept { // erase all _Eos(0); } _CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, const basic_string& _Right) { // replace [_Off, _Off + _Nx) with _Right return _Replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } _CONSTEXPR20 basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); return _Replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr() + _Roff, _Count); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { // replace [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return _Replace(_Off, _Nx, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> _CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.substr(static_cast(_Roff), static_cast(_Count))); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& replace( const size_type _Off, size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) return _Replace(_Off, _Nx, _Ptr, _Count); } _CONSTEXPR20 basic_string& replace(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { // replace [_Off, _Off + _Nx) with [_Ptr, ) return _Replace(_Off, _Nx, _Ptr, _Convert_size(_Traits::length(_Ptr))); } _CONSTEXPR20 basic_string& replace(const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { // replace [_Off, _Off + _Nx) with _Count * _Ch _Mypair._Myval2._Check_offset(_Off); _Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx); if (_Count == _Nx) { _Traits::assign(_Mypair._Myval2._Myptr() + _Off, static_cast(_Count), _Ch); return *this; } const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count < _Nx || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { // either we are shrinking, or the growth fits // may temporarily overflow; OK because size_type must be unsigned const auto _New_size = static_cast(_Old_size + _Count - _Nx); _ASAN_STRING_REMOVE(*this); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; _Traits::move(_Insert_at + _Count, _Insert_at + _Nx, static_cast(_Old_size - _Nx - _Off + 1)); _Traits::assign(_Insert_at, static_cast(_Count), _Ch); _ASAN_STRING_CREATE(*this); return *this; } return _Reallocate_grow_by( static_cast(_Count - _Nx), [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off, const size_type _Nx, const size_type _Count, const _Elem _Ch) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Off)); _Traits::assign(_New_ptr + _Off, static_cast(_Count), _Ch); _Traits::copy( _New_ptr + _Off + _Count, _Old_ptr + _Off + _Nx, static_cast(_Old_size - _Nx - _Off + 1)); }, _Off, _Nx, _Count, _Ch); } _CONSTEXPR20 basic_string& replace( const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { // replace [_First, _Last) with _Right _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 return replace(static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()), static_cast(_Last._Ptr - _First._Ptr), _Right); } #if _HAS_CXX17 template = 0> _CONSTEXPR20 basic_string& replace( const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { // replace [_First, _Last) with _Right _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 return replace(static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()), static_cast(_Last._Ptr - _First._Ptr), _Right); } #endif // _HAS_CXX17 _CONSTEXPR20 basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_First, _Last) with [_Ptr, _Ptr + _Count) _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 return replace(static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()), static_cast(_Last._Ptr - _First._Ptr), _Ptr, _Count); } _CONSTEXPR20 basic_string& replace( const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { // replace [_First, _Last) with [_Ptr, ) _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 return replace(static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()), static_cast(_Last._Ptr - _First._Ptr), _Ptr); } _CONSTEXPR20 basic_string& replace( const const_iterator _First, const const_iterator _Last, const size_type _Count, const _Elem _Ch) { // replace [_First, _Last) with _Count * _Ch _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 return replace(static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()), static_cast(_Last._Ptr - _First._Ptr), _Count, _Ch); } template , int> = 0> _CONSTEXPR20 basic_string& replace( const const_iterator _First, const const_iterator _Last, const _Iter _First2, const _Iter _Last2) { // replace [_First, _Last) with [_First2, _Last2), input iterators _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_STD _Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()); const auto _Length = static_cast(_Last._Ptr - _First._Ptr); _STD _Adl_verify_range(_First2, _Last2); const auto _UFirst2 = _STD _Get_unwrapped(_First2); const auto _ULast2 = _STD _Get_unwrapped(_Last2); if constexpr (_Is_elem_cvptr::value) { return _Replace( _Off, _Length, _UFirst2, _STD _Convert_size(static_cast(_ULast2 - _UFirst2))); } else { const basic_string _Right(_UFirst2, _ULast2, get_allocator()); return _Replace(_Off, _Length, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } } #if _HAS_CXX23 template <_Container_compatible_range<_Elem> _Rng> constexpr basic_string& replace_with_range(const const_iterator _First, const const_iterator _Last, _Rng&& _Range) { _STD _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 const auto _Off = static_cast(_Unfancy(_First._Ptr) - _Mypair._Myval2._Myptr()); const auto _Length = static_cast(_Last._Ptr - _First._Ptr); if constexpr (_RANGES sized_range<_Rng> && _Contiguous_range_of<_Rng, _Elem>) { const auto _Count = _Convert_size(_To_unsigned_like(_RANGES size(_Range))); return _Replace(_Off, _Length, _RANGES data(_Range), _Count); } else { const basic_string _Right(from_range, _Range, get_allocator()); return _Replace(_Off, _Length, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } } #endif // _HAS_CXX23 _NODISCARD _CONSTEXPR20 iterator begin() noexcept { return iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } _NODISCARD _CONSTEXPR20 const_iterator begin() const noexcept { return const_iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } _NODISCARD _CONSTEXPR20 iterator end() noexcept { return iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } _NODISCARD _CONSTEXPR20 const_iterator end() const noexcept { return const_iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } _NODISCARD _CONSTEXPR20 _Elem* _Unchecked_begin() noexcept { return _Mypair._Myval2._Myptr(); } _NODISCARD _CONSTEXPR20 const _Elem* _Unchecked_begin() const noexcept { return _Mypair._Myval2._Myptr(); } _NODISCARD _CONSTEXPR20 _Elem* _Unchecked_end() noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } _NODISCARD _CONSTEXPR20 const _Elem* _Unchecked_end() const noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } _NODISCARD _CONSTEXPR20 reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } _NODISCARD _CONSTEXPR20 const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } _NODISCARD _CONSTEXPR20 reverse_iterator rend() noexcept { return reverse_iterator(begin()); } _NODISCARD _CONSTEXPR20 const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } _NODISCARD _CONSTEXPR20 const_iterator cbegin() const noexcept { return begin(); } _NODISCARD _CONSTEXPR20 const_iterator cend() const noexcept { return end(); } _NODISCARD _CONSTEXPR20 const_reverse_iterator crbegin() const noexcept { return rbegin(); } _NODISCARD _CONSTEXPR20 const_reverse_iterator crend() const noexcept { return rend(); } _CONSTEXPR20 void shrink_to_fit() { // reduce capacity auto& _My_data = _Mypair._Myval2; if (!_My_data._Large_mode_engaged()) { // can't shrink from small mode return; } if (_My_data._Mysize <= _Small_string_capacity) { _Become_small(); return; } size_type _Target_capacity = (_STD min) (static_cast(_My_data._Mysize | _Alloc_mask), max_size()); if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto& _Al = _Getal(); const pointer _New_ptr = _Allocate_for_capacity<_Allocation_policy::_Exactly>(_Al, _Target_capacity); // throws _ASAN_STRING_REMOVE(*this); _My_data._Orphan_all(); _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_My_data._Bx._Ptr), static_cast(_My_data._Mysize + 1)); _Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres); _My_data._Bx._Ptr = _New_ptr; _My_data._Myres = _Target_capacity; _ASAN_STRING_CREATE(*this); } } _NODISCARD _CONSTEXPR20 reference at(const size_type _Off) { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } _NODISCARD _CONSTEXPR20 const_reference at(const size_type _Off) const { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } _NODISCARD _CONSTEXPR20 reference operator[](const size_type _Off) noexcept /* strengthened */ { #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif return _Mypair._Myval2._Myptr()[_Off]; } _NODISCARD _CONSTEXPR20 const_reference operator[](const size_type _Off) const noexcept /* strengthened */ { #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif return _Mypair._Myval2._Myptr()[_Off]; } #if _HAS_CXX17 /* implicit */ _CONSTEXPR20 operator basic_string_view<_Elem, _Traits>() const noexcept { // return a string_view around *this's character-type sequence return basic_string_view<_Elem, _Traits>{ _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)}; } #endif // _HAS_CXX17 _CONSTEXPR20 void push_back(const _Elem _Ch) { // insert element at end const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Old_size < _Mypair._Myval2._Myres) { const auto _New_size = static_cast(_Old_size + 1); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[_Old_size], _Ch); _Traits::assign(_Ptr[_Old_size + 1], _Elem()); return; } _Reallocate_grow_by( 1, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const _Elem _Ch) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Old_size)); _Traits::assign(_New_ptr[_Old_size], _Ch); _Traits::assign(_New_ptr[_Old_size + 1], _Elem()); }, _Ch); } _CONSTEXPR20 void pop_back() noexcept /* strengthened */ { const size_type _Old_size = _Mypair._Myval2._Mysize; #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Old_size != 0, "pop_back() called on empty string"); #endif _Eos(static_cast(_Old_size - 1)); } _NODISCARD _CONSTEXPR20 reference front() noexcept /* strengthened */ { #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif return _Mypair._Myval2._Myptr()[0]; } _NODISCARD _CONSTEXPR20 const_reference front() const noexcept /* strengthened */ { #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif return _Mypair._Myval2._Myptr()[0]; } _NODISCARD _CONSTEXPR20 reference back() noexcept /* strengthened */ { #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } _NODISCARD _CONSTEXPR20 const_reference back() const noexcept /* strengthened */ { #if _MSVC_STL_HARDENING_BASIC_STRING || _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } _NODISCARD _CONSTEXPR20 _Ret_z_ const _Elem* c_str() const noexcept { return _Mypair._Myval2._Myptr(); } _NODISCARD _CONSTEXPR20 _Ret_z_ const _Elem* data() const noexcept { return _Mypair._Myval2._Myptr(); } #if _HAS_CXX17 _NODISCARD _CONSTEXPR20 _Ret_z_ _Elem* data() noexcept { return _Mypair._Myval2._Myptr(); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type length() const noexcept { return _Mypair._Myval2._Mysize; } _NODISCARD _CONSTEXPR20 size_type size() const noexcept { return _Mypair._Myval2._Mysize; } _NODISCARD _CONSTEXPR20 size_type max_size() const noexcept { const size_type _Alloc_max = _Alty_traits::max_size(_Getal()); const size_type _Storage_max = // can always store small string (_STD max) (_Alloc_max, static_cast(_BUF_SIZE)); return (_STD min) (static_cast(_STD _Max_limit()), static_cast(_Storage_max - 1) // -1 is for null terminator and/or npos ); } _CONSTEXPR20 void resize(_CRT_GUARDOVERFLOW const size_type _New_size, const _Elem _Ch = _Elem()) { // determine new length, padding with _Ch elements as needed const size_type _Old_size = size(); if (_New_size <= _Old_size) { _Eos(_New_size); } else { append(static_cast(_New_size - _Old_size), _Ch); } } template constexpr void #if _HAS_CXX23 resize_and_overwrite #else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv _Resize_and_overwrite #endif // ^^^ !_HAS_CXX23 ^^^ (_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) { if (_Mypair._Myval2._Myres < _New_size) { _Reallocate_grow_by(static_cast(_New_size - _Mypair._Myval2._Mysize), [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Old_size + 1)); }); } else { _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _New_size); _Mypair._Myval2._Mysize = _New_size; } auto _Arg_ptr = _Mypair._Myval2._Myptr(); auto _Arg_size = _New_size; const auto _Result_size = _STD move(_Op)(_Arg_ptr, _Arg_size); using _Result_type = remove_const_t; static_assert(_Integer_like<_Result_type>, "the return type of the operation must be integer-like, N5014 [string.capacity]/8"); const auto _Result_as_size_type = static_cast(_Result_size); #if _ITERATOR_DEBUG_LEVEL != 0 if constexpr (_Signed_integer_like<_Result_type>) { _STL_VERIFY(_Result_size >= 0, "the returned size can't be smaller than 0, N5014 [string.capacity]/9.2"); } _STL_VERIFY(_Result_as_size_type <= _New_size, "the returned size can't be greater than the passed size, N5014 [string.capacity]/9.3"); #endif // _ITERATOR_DEBUG_LEVEL != 0 _Eos(_Result_as_size_type); } #if _HAS_CXX23 template constexpr void _Resize_and_overwrite(_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) { resize_and_overwrite(_New_size, _Op); } #endif // _HAS_CXX23 _NODISCARD _CONSTEXPR20 size_type capacity() const noexcept { return _Mypair._Myval2._Myres; } #if _HAS_CXX20 constexpr void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { // determine new minimum length of allocated storage if (_Mypair._Myval2._Myres >= _Newcap) { // requested capacity is not larger than current capacity, ignore return; // nothing to do } const size_type _Old_size = _Mypair._Myval2._Mysize; _Reallocate_grow_by(static_cast(_Newcap - _Old_size), [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Old_size + 1)); }); // `_Reallocate_grow_by` calls `_ASAN_STRING_CREATE` assuming that the string // has size (initialized memory) equal to its new capacity (allocated memory). // This is not true for the `reserve` method, so we modify the ASan annotation. _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _Old_size); _Mypair._Myval2._Mysize = _Old_size; } _CXX20_DEPRECATE_STRING_RESERVE_WITHOUT_ARGUMENT void reserve() { if (_Mypair._Myval2._Mysize == 0 && _Mypair._Myval2._Large_mode_engaged()) { _Become_small(); } } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap = 0) { // determine new minimum length of allocated storage if (_Mypair._Myval2._Mysize > _Newcap) { // requested capacity is not large enough for current size, ignore return; // nothing to do } if (_Mypair._Myval2._Myres == _Newcap) { // we're already at the requested capacity return; // nothing to do } if (_Mypair._Myval2._Myres < _Newcap) { // reallocate to grow const size_type _Old_size = _Mypair._Myval2._Mysize; _Reallocate_grow_by(static_cast(_Newcap - _Old_size), [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Old_size + 1)); }); // `_Reallocate_grow_by` calls `_ASAN_STRING_CREATE` assuming that the string // has size (initialized memory) equal to its new capacity (allocated memory). // This is not true for the `reserve` method, so we modify the ASan annotation. _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _Old_size); _Mypair._Myval2._Mysize = _Old_size; return; } if (_Newcap <= _Small_string_capacity && _Mypair._Myval2._Large_mode_engaged()) { // deallocate everything; switch back to "small" mode _Become_small(); return; } // ignore requests to reserve to [_Small_string_capacity + 1, _Myres) } #endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD_EMPTY_MEMBER _CONSTEXPR20 bool empty() const noexcept { return _Mypair._Myval2._Mysize == 0; } _CONSTEXPR20 size_type copy( _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); _Traits::copy(_Ptr, _Mypair._Myval2._Myptr() + _Off, static_cast(_Count)); return _Count; } _CONSTEXPR20 _Pre_satisfies_(_Dest_size >= _Count) size_type _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count, const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Dest_size) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); _Traits::_Copy_s(_Dest, _Dest_size, _Mypair._Myval2._Myptr() + _Off, _Count); return _Count; } static _CONSTEXPR20 void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) noexcept { // exchange a string in large mode with one in small mode const pointer _Ptr = _Starts_large._Bx._Ptr; _Starts_large._Bx._Switch_to_buf(); _Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE); _Construct_in_place(_Starts_small._Bx._Ptr, _Ptr); } _CONSTEXPR20 void _Swap_data(basic_string& _Right) noexcept { using _STD swap; auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; #if !defined(_INSERT_STRING_ANNOTATION) if constexpr (_Can_memcpy_val) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { const auto _My_data_mem = reinterpret_cast(_STD addressof(_My_data)) + _Memcpy_val_offset; const auto _Right_data_mem = reinterpret_cast(_STD addressof(_Right_data)) + _Memcpy_val_offset; unsigned char _Temp_mem[_Memcpy_val_size]; _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); return; } } #endif // ^^^ !defined(_INSERT_STRING_ANNOTATION) ^^^ const bool _My_large = _My_data._Large_mode_engaged(); const bool _Right_large = _Right_data._Large_mode_engaged(); if (_My_large && _Right_large) { // swap buffers, iterators preserved swap(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); // intentional ADL } else if (_My_large) { // swap large with small _Swap_bx_large_with_small(_My_data, _Right_data); } else if (_Right_large) { // swap small with large _Swap_bx_large_with_small(_Right_data, _My_data); } else { _Elem _Temp_buf[_BUF_SIZE]; _Traits::copy(_Temp_buf, _My_data._Bx._Buf, static_cast(_My_data._Mysize + 1)); _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, static_cast(_Right_data._Mysize + 1)); _Traits::copy(_Right_data._Bx._Buf, _Temp_buf, static_cast(_My_data._Mysize + 1)); } _STD swap(_My_data._Mysize, _Right_data._Mysize); _STD swap(_My_data._Myres, _Right_data._Myres); } _CONSTEXPR20 void swap(basic_string& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); #if _ITERATOR_DEBUG_LEVEL != 0 auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; if (!_My_data._Large_mode_engaged()) { _My_data._Orphan_all(); } if (!_Right_data._Large_mode_engaged()) { _Right_data._Orphan_all(); } _My_data._Swap_proxy_and_iterators(_Right_data); #endif // _ITERATOR_DEBUG_LEVEL != 0 _Swap_data(_Right); } } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const noexcept(_Is_nothrow_convertible_v>) { // look for _Right beginning at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find(const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for _Right beginning at or after _Off return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } _NODISCARD _CONSTEXPR20 size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning at or after _Off return static_cast( _Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } _NODISCARD _CONSTEXPR20 size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning at or after _Off return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept { // look for _Ch at or after _Off return static_cast(_Traits_find_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const noexcept(_Is_nothrow_convertible_v>) { // look for _Right beginning before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type rfind(const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for _Right beginning before _Off return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } _NODISCARD _CONSTEXPR20 size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning before _Off return static_cast( _Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } _NODISCARD _CONSTEXPR20 size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning before _Off return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept { // look for _Ch before _Off return static_cast(_Traits_rfind_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type find_first_of(const _StringViewIsh& _Right, const size_type _Off = 0) const noexcept(_Is_nothrow_convertible_v>) { // look for one of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find_first_of( const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } _NODISCARD _CONSTEXPR20 size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) at or after _Off return static_cast( _Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } _NODISCARD _CONSTEXPR20 size_type find_first_of( _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { // look for _Ch at or after _Off return static_cast(_Traits_find_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type find_last_of(const _StringViewIsh& _Right, const size_type _Off = npos) const noexcept(_Is_nothrow_convertible_v>) { // look for one of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find_last_of(const basic_string& _Right, size_type _Off = npos) const noexcept { // look for one of _Right before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } _NODISCARD _CONSTEXPR20 size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) before _Off return static_cast( _Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } _NODISCARD _CONSTEXPR20 size_type find_last_of( _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for one of [_Ptr, ) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { // look for _Ch before _Off return static_cast(_Traits_rfind_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type find_first_not_of(const _StringViewIsh& _Right, const size_type _Off = 0) const noexcept(_Is_nothrow_convertible_v>) { // look for none of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find_first_not_of( const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } _NODISCARD _CONSTEXPR20 size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) at or after _Off return static_cast( _Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } _NODISCARD _CONSTEXPR20 size_type find_first_not_of( _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept { // look for non-_Ch at or after _Off return static_cast(_Traits_find_not_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 size_type find_last_not_of(const _StringViewIsh& _Right, const size_type _Off = npos) const noexcept(_Is_nothrow_convertible_v>) { // look for none of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _As_view.data(), _As_view.size())); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 size_type find_last_not_of( const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for none of _Right before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize))); } _NODISCARD _CONSTEXPR20 size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) before _Off return static_cast( _Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, static_cast(_Count))); } _NODISCARD _CONSTEXPR20 size_type find_last_not_of( _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for none of [_Ptr, ) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ptr, _Traits::length(_Ptr))); } _NODISCARD _CONSTEXPR20 size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept { // look for non-_Ch before _Off return static_cast(_Traits_rfind_not_ch<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), static_cast(_Off), _Ch)); } #if _HAS_CXX17 _NODISCARD bool _Starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { // Used exclusively by filesystem return basic_string_view<_Elem, _Traits>(*this)._Starts_with(_Right); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 basic_string substr(const size_type _Off = 0, const size_type _Count = npos) #if _HAS_CXX23 const& #else const #endif { // return [_Off, _Off + _Count) as new string, default-constructing its allocator return basic_string{*this, _Off, _Count}; } #if _HAS_CXX23 _NODISCARD constexpr basic_string substr(const size_type _Off = 0, const size_type _Count = npos) && { // return [_Off, _Off + _Count) as new string, potentially moving, default-constructing its allocator return basic_string{_STD move(*this), _Off, _Count}; } #endif // _HAS_CXX23 _CONSTEXPR20 bool _Equal(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize)); } _CONSTEXPR20 bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { // compare [0, size()) with _Ptr for equality return _Traits_equal<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), _Ptr, _Traits::length(_Ptr)); } #if _HAS_CXX17 template = 0> _NODISCARD _CONSTEXPR20 int compare(const _StringViewIsh& _Right) const noexcept(_Is_nothrow_convertible_v>) { // compare [0, size()) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), _As_view.data(), _As_view.size()); } template = 0> _NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { // compare [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, static_cast(_Mypair._Myval2._Clamp_suffix_size(_Off, _Nx)), _As_view.data(), _As_view.size()); } template = 0> _NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); const auto _With_substr = _As_view.substr(static_cast(_Roff), static_cast(_Count)); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, static_cast(_Mypair._Myval2._Clamp_suffix_size(_Off, _Nx)), _With_substr.data(), _With_substr.size()); } #endif // _HAS_CXX17 _NODISCARD _CONSTEXPR20 int compare(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize)); } _NODISCARD _CONSTEXPR20 int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { // compare [_Off, _Off + _Nx) with _Right _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, static_cast(_Mypair._Myval2._Clamp_suffix_size(_Off, _Nx)), _Right._Mypair._Myval2._Myptr(), static_cast(_Right._Mypair._Myval2._Mysize)); } _NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Mypair._Myval2._Check_offset(_Off); _Right._Mypair._Myval2._Check_offset(_Roff); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, static_cast(_Mypair._Myval2._Clamp_suffix_size(_Off, _Nx)), _Right._Mypair._Myval2._Myptr() + _Roff, static_cast(_Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count))); } _NODISCARD _CONSTEXPR20 int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { // compare [0, size()) with [_Ptr, ) return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize), _Ptr, _Traits::length(_Ptr)); } _NODISCARD _CONSTEXPR20 int compare( const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { // compare [_Off, _Off + _Nx) with [_Ptr, ) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, static_cast(_Mypair._Myval2._Clamp_suffix_size(_Off, _Nx)), _Ptr, _Traits::length(_Ptr)); } _NODISCARD _CONSTEXPR20 int compare(const size_type _Off, const size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const { // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, static_cast(_Mypair._Myval2._Clamp_suffix_size(_Off, _Nx)), _Ptr, static_cast(_Count)); } #if _HAS_CXX20 _NODISCARD constexpr bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .starts_with(_Right); } _NODISCARD constexpr bool starts_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .starts_with(_Right); } _NODISCARD constexpr bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .starts_with(_Right); } _NODISCARD constexpr bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .ends_with(_Right); } _NODISCARD constexpr bool ends_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .ends_with(_Right); } _NODISCARD constexpr bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .ends_with(_Right); } #endif // _HAS_CXX20 #if _HAS_CXX23 _NODISCARD constexpr bool contains(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .contains(_Right); } _NODISCARD constexpr bool contains(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .contains(_Right); } _NODISCARD constexpr bool contains(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>{_Mypair._Myval2._Myptr(), static_cast(_Mypair._Myval2._Mysize)} .contains(_Right); } #endif // _HAS_CXX23 _NODISCARD _CONSTEXPR20 allocator_type get_allocator() const noexcept { return static_cast(_Getal()); } private: _NODISCARD static _CONSTEXPR20 size_type _Calculate_growth( const size_type _Requested, const size_type _Old, const size_type _Max) noexcept { const auto _Masked = static_cast(_Requested | _Alloc_mask); if (_Masked > _Max) { // the mask overflows, settle for max_size() return _Max; } if (_Old > _Max - _Old / 2) { // similarly, geometric overflows return _Max; } return (_STD max) (_Masked, static_cast(_Old + _Old / 2)); } _NODISCARD _CONSTEXPR20 size_type _Calculate_growth(const size_type _Requested) const noexcept { return _Calculate_growth(_Requested, _Mypair._Myval2._Myres, max_size()); } template _CONSTEXPR20 basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { // reallocate to store exactly _New_size elements, new buffer prepared by // _Fn(_New_ptr, _New_size, _Args...) if (_New_size > max_size()) { _Xlen_string(); // result too long } const size_type _Old_capacity = _Mypair._Myval2._Myres; size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws _Mypair._Myval2._Orphan_all(); _ASAN_STRING_REMOVE(*this); _Mypair._Myval2._Mysize = _New_size; _Mypair._Myval2._Myres = _New_capacity; _Fn(_Unfancy(_New_ptr), _New_size, _Args...); if (_Old_capacity > _Small_string_capacity) { _Deallocate_for_capacity(_Al, _Mypair._Myval2._Bx._Ptr, _Old_capacity); _Mypair._Myval2._Bx._Ptr = _New_ptr; } else { _Construct_in_place(_Mypair._Myval2._Bx._Ptr, _New_ptr); } _ASAN_STRING_CREATE(*this); return *this; } template _CONSTEXPR20 basic_string& _Reallocate_grow_by(const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { // reallocate to increase size by _Size_increase elements, new buffer prepared by // _Fn(_New_ptr, _Old_ptr, _Old_size, _Args...) auto& _My_data = _Mypair._Myval2; const size_type _Old_size = _My_data._Mysize; if (max_size() - _Old_size < _Size_increase) { _Xlen_string(); // result too long } const auto _New_size = static_cast(_Old_size + _Size_increase); const size_type _Old_capacity = _My_data._Myres; size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws _My_data._Orphan_all(); _ASAN_STRING_REMOVE(*this); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; _Elem* const _Raw_new = _Unfancy(_New_ptr); if (_Old_capacity > _Small_string_capacity) { const pointer _Old_ptr = _My_data._Bx._Ptr; _Fn(_Raw_new, _Unfancy(_Old_ptr), _Old_size, _Args...); _Deallocate_for_capacity(_Al, _Old_ptr, _Old_capacity); _My_data._Bx._Ptr = _New_ptr; } else { _Fn(_Raw_new, _My_data._Bx._Buf, _Old_size, _Args...); _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); } _ASAN_STRING_CREATE(*this); return *this; } _CONSTEXPR20 void _Become_small() noexcept { // release any held storage and return to small string mode auto& _My_data = _Mypair._Myval2; _STL_INTERNAL_CHECK(_My_data._Large_mode_engaged()); _STL_INTERNAL_CHECK(_My_data._Mysize <= _Small_string_capacity); _My_data._Orphan_all(); _ASAN_STRING_REMOVE(*this); const pointer _Ptr = _My_data._Bx._Ptr; _My_data._Bx._Switch_to_buf(); _Traits::copy(_My_data._Bx._Buf, _Unfancy(_Ptr), static_cast(_My_data._Mysize + 1)); auto& _Al = _Getal(); _Deallocate_for_capacity(_Al, _Ptr, _My_data._Myres); _My_data._Myres = _Small_string_capacity; } _CONSTEXPR20 void _Eos(const size_type _New_size) noexcept { // set new length and null terminator _ASAN_STRING_MODIFY(*this, _Mypair._Myval2._Mysize, _New_size); _Mypair._Myval2._Mysize = _New_size; _Traits::assign(_Mypair._Myval2._Myptr()[_New_size], _Elem()); } template _CONSTEXPR20 basic_string& _Append( _In_reads_(_Count) const _UElem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // append [_Ptr, _Ptr + _Count), handling volatile-qualified _Elem _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_UElem, _Elem, volatile _Elem>); const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { const auto _New_size = static_cast(_Old_size + _Count); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _STD _Traits_move_batch<_Traits>(_Old_ptr + _Old_size, _Ptr, static_cast(_Count)); _Traits::assign(_Old_ptr[_Old_size + _Count], _Elem()); return *this; } return _Reallocate_grow_by( _Count, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const _UElem* const _Ptr, const size_type _Count) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Old_size)); _STD _Traits_copy_batch<_Traits>(_New_ptr + _Old_size, _Ptr, static_cast(_Count)); _Traits::assign(_New_ptr[_Old_size + _Count], _Elem()); }, _Ptr, _Count); } template _CONSTEXPR20 basic_string& _Assign( _In_reads_(_Count) const _UElem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count), handling volatile-qualified _Elem _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_UElem, _Elem, volatile _Elem>); if (_Count <= _Mypair._Myval2._Myres) { _ASAN_STRING_REMOVE(*this); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; _STD _Traits_move_batch<_Traits>(_Old_ptr, _Ptr, static_cast(_Count)); _Traits::assign(_Old_ptr[_Count], _Elem()); _ASAN_STRING_CREATE(*this); return *this; } return _Reallocate_for( _Count, [](_Elem* const _New_ptr, const size_type _Count, const _UElem* const _Ptr) static { _STD _Traits_copy_batch<_Traits>(_New_ptr, _Ptr, static_cast(_Count)); _Traits::assign(_New_ptr[_Count], _Elem()); }, _Ptr); } template _CONSTEXPR20 basic_string& _Insert( const size_type _Off, _In_reads_(_Count) const _UElem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // insert [_Ptr, _Ptr + _Count) at _Off, handling volatile-qualified _Elem _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_UElem, _Elem, volatile _Elem>); _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; // We can't check for overlapping ranges when constant evaluated since comparison of pointers into string // literals is unspecified, so always reallocate and copy to the new buffer if constant evaluated. const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size; if (_Check_overlap && !_STD _Is_constant_evaluated()) { const auto _New_size = static_cast(_Old_size + _Count); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; // the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out, // while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count size_type _Ptr_shifted_after; if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { // inserted content is before the shifted region, or does not alias _Ptr_shifted_after = _Count; // none of _Ptr's data shifts } else if (_Insert_at <= _Ptr) { // all of [_Ptr, _Ptr + _Count) shifts _Ptr_shifted_after = 0; } else { // [_Ptr, _Ptr + _Count) contains _Insert_at, so only the part after _Insert_at shifts _Ptr_shifted_after = static_cast(_Insert_at - _Ptr); } _Traits::move( _Insert_at + _Count, _Insert_at, static_cast(_Old_size - _Off + 1)); // move suffix + null down _STD _Traits_copy_batch<_Traits>(_Insert_at, _Ptr, static_cast(_Ptr_shifted_after)); _STD _Traits_copy_batch<_Traits>(_Insert_at + _Ptr_shifted_after, _Ptr + _Count + _Ptr_shifted_after, static_cast(_Count - _Ptr_shifted_after)); return *this; } return _Reallocate_grow_by( _Count, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off, const _UElem* const _Ptr, const size_type _Count) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Off)); _STD _Traits_copy_batch<_Traits>(_New_ptr + _Off, _Ptr, static_cast(_Count)); _Traits::copy(_New_ptr + _Off + _Count, _Old_ptr + _Off, static_cast(_Old_size - _Off + 1)); }, _Off, _Ptr, _Count); } template _CONSTEXPR20 basic_string& _Replace( const size_type _Off, size_type _Nx, _In_reads_(_Count) const _UElem* const _Ptr, const size_type _Count) { // replace [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count), handling volatile-qualified _Elem _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_UElem, _Elem, volatile _Elem>); _Mypair._Myval2._Check_offset(_Off); _Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx); if (_Nx == _Count) { // size doesn't change, so a single move does the trick _STD _Traits_move_batch<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Ptr, static_cast(_Count)); return *this; } const size_type _Old_size = _Mypair._Myval2._Mysize; const auto _Suffix_size = static_cast(_Old_size - _Nx - _Off + 1); if (_Count < _Nx) { // suffix shifts backwards; we don't have to move anything out of the way _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; _STD _Traits_move_batch<_Traits>(_Insert_at, _Ptr, static_cast(_Count)); _Traits::move(_Insert_at + _Count, _Insert_at + _Nx, static_cast(_Suffix_size)); const auto _New_size = static_cast(_Old_size - (_Nx - _Count)); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; return *this; } const size_type _Growth = static_cast(_Count - _Nx); // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate // and copy to the new buffer if constant evaluated #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits const auto _New_size = static_cast(_Old_size + _Growth); _ASAN_STRING_MODIFY(*this, _Old_size, _New_size); _Mypair._Myval2._Mysize = _New_size; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; _Elem* const _Suffix_at = _Insert_at + _Nx; size_type _Ptr_shifted_after; // see rationale in insert if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { _Ptr_shifted_after = _Count; } else if (_Suffix_at <= _Ptr) { _Ptr_shifted_after = 0; } else { _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); } _Traits::move(_Suffix_at + _Growth, _Suffix_at, static_cast(_Suffix_size)); // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; // this case doesn't occur in insert because the new content must come from outside the removed // content there (because in insert there is no removed content) _STD _Traits_move_batch<_Traits>(_Insert_at, _Ptr, static_cast(_Ptr_shifted_after)); // the next case can be copy, because it comes from the chunk moved out of the way in the // first move, and the hole we're filling can't alias the chunk we moved out of the way _STD _Traits_copy_batch<_Traits>(_Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, static_cast(_Count - _Ptr_shifted_after)); return *this; } } return _Reallocate_grow_by( _Growth, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const size_type _Off, const size_type _Nx, const _UElem* const _Ptr, const size_type _Count) static { _Traits::copy(_New_ptr, _Old_ptr, static_cast(_Off)); _STD _Traits_copy_batch<_Traits>(_New_ptr + _Off, _Ptr, static_cast(_Count)); _Traits::copy( _New_ptr + _Off + _Count, _Old_ptr + _Off + _Nx, static_cast(_Old_size - _Nx - _Off + 1)); }, _Off, _Nx, _Ptr, _Count); } _CONSTEXPR20 void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage auto& _My_data = _Mypair._Myval2; _My_data._Orphan_all(); if (_My_data._Large_mode_engaged()) { _ASAN_STRING_REMOVE(*this); auto& _Al = _Getal(); _Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres); _My_data._Bx._Switch_to_buf(); } _My_data._Mysize = 0; _My_data._Myres = _Small_string_capacity; // the _Traits::assign is last so the codegen doesn't think the char write can alias this _Traits::assign(_My_data._Bx._Buf[0], _Elem()); } public: _CONSTEXPR20 void _Orphan_all() noexcept { // used by filesystem::path _Mypair._Myval2._Orphan_all(); } private: _CONSTEXPR20 void _Swap_proxy_and_iterators(basic_string& _Right) noexcept { _Mypair._Myval2._Swap_proxy_and_iterators(_Right._Mypair._Myval2); } _CONSTEXPR20 _Alty& _Getal() noexcept { return _Mypair._Get_first(); } _CONSTEXPR20 const _Alty& _Getal() const noexcept { return _Mypair._Get_first(); } _Compressed_pair<_Alty, _Scary_val> _Mypair; }; #pragma warning(pop) #if _HAS_CXX23 template constexpr bool _Equivalence_is_equality_impl> = _Is_implementation_handled_char_traits<_Traits>; #endif // _HAS_CXX23 #if _HAS_CXX17 template >, enable_if_t, _Is_allocator<_Alloc>>, int> = 0> basic_string(_Iter, _Iter, _Alloc = _Alloc()) -> basic_string<_Iter_value_t<_Iter>, char_traits<_Iter_value_t<_Iter>>, _Alloc>; template , enable_if_t<_Is_allocator<_Alloc>::value, int> = 0> explicit basic_string(basic_string_view<_Elem, _Traits>, const _Alloc& = _Alloc()) -> basic_string<_Elem, _Traits, _Alloc>; template , enable_if_t<_Is_allocator<_Alloc>::value, int> = 0> basic_string(basic_string_view<_Elem, _Traits>, _Guide_size_type_t<_Alloc>, _Guide_size_type_t<_Alloc>, const _Alloc& = _Alloc()) -> basic_string<_Elem, _Traits, _Alloc>; #if _HAS_CXX23 template <_RANGES input_range _Rng, _Allocator_for_container _Alloc = allocator<_RANGES range_value_t<_Rng>>> basic_string(from_range_t, _Rng&&, _Alloc = _Alloc()) -> basic_string<_RANGES range_value_t<_Rng>, char_traits<_RANGES range_value_t<_Rng>>, _Alloc>; #endif // _HAS_CXX23 #endif // _HAS_CXX17 _EXPORT_STD template _CONSTEXPR20 void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { _Left.swap(_Right); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Left_size = _Left.size(); const auto _Right_size = _Right.size(); if (_Left.max_size() - _Left_size < _Right_size) { _Xlen_string(); } return {_String_constructor_concat_tag{}, _Left, _Left.c_str(), _Left_size, _Right.c_str(), _Right_size}; } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Convert_size<_Size_type>(_Traits::length(_Left)); const auto _Right_size = _Right.size(); if (_Right.max_size() - _Right_size < _Left_size) { _Xlen_string(); } return {_String_constructor_concat_tag{}, _Right, _Left, _Left_size, _Right.c_str(), _Right_size}; } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Right_size = _Right.size(); if (_Right_size == _Right.max_size()) { _Xlen_string(); } return {_String_constructor_concat_tag{}, _Right, _STD addressof(_Left), 1, _Right.c_str(), _Right_size}; } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Left.size(); const auto _Right_size = _Convert_size<_Size_type>(_Traits::length(_Right)); if (_Left.max_size() - _Left_size < _Right_size) { _Xlen_string(); } return {_String_constructor_concat_tag{}, _Left, _Left.c_str(), _Left_size, _Right, _Right_size}; } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const _Elem _Right) { const auto _Left_size = _Left.size(); if (_Left_size == _Left.max_size()) { _Xlen_string(); } return {_String_constructor_concat_tag{}, _Left, _Left.c_str(), _Left_size, _STD addressof(_Right), 1}; } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { return _STD move(_Left.append(_Right)); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_STD addressof(_Left) != _STD addressof(_Right), "You cannot concatenate the same moved string to itself. See N4950 [res.on.arguments]/1.3: " "If a function argument is bound to an rvalue reference parameter, the implementation may assume that " "this parameter is a unique reference to this argument, except that the argument passed to " "a move-assignment operator may be a reference to *this ([lib.types.movedfrom])."); #endif // _ITERATOR_DEBUG_LEVEL == 2 return {_String_constructor_concat_tag{}, _Left, _Right}; } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, 1, _Left)); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, _In_z_ const _Elem* const _Right) { return _STD move(_Left.append(_Right)); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const _Elem _Right) { _Left.push_back(_Right); return _STD move(_Left); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 bool operator==( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return _Left._Equal(_Right); } _EXPORT_STD template _NODISCARD _CONSTEXPR20 bool operator==( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return _Left._Equal(_Right); } #if _HAS_CXX20 _EXPORT_STD template _NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); } _EXPORT_STD template _NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { return _Right._Equal(_Left); } template _NODISCARD bool operator!=( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return !(_Left == _Right); } template _NODISCARD bool operator!=(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { return !(_Left == _Right); } template _NODISCARD bool operator!=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return !(_Left == _Right); } template _NODISCARD bool operator<( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return _Left.compare(_Right) < 0; } template _NODISCARD bool operator<(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { return _Right.compare(_Left) > 0; } template _NODISCARD bool operator<(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return _Left.compare(_Right) < 0; } template _NODISCARD bool operator>( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return _Right < _Left; } template _NODISCARD bool operator>(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { return _Right < _Left; } template _NODISCARD bool operator>(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return _Right < _Left; } template _NODISCARD bool operator<=( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return !(_Right < _Left); } template _NODISCARD bool operator<=(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { return !(_Right < _Left); } template _NODISCARD bool operator<=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return !(_Right < _Left); } template _NODISCARD bool operator>=( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return !(_Left < _Right); } template _NODISCARD bool operator>=(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { return !(_Left < _Right); } template _NODISCARD bool operator>=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) noexcept /* strengthened */ { return !(_Left < _Right); } #endif // ^^^ !_HAS_CXX20 ^^^ _EXPORT_STD using string = basic_string, allocator>; _EXPORT_STD using wstring = basic_string, allocator>; #ifdef __cpp_lib_char8_t _EXPORT_STD using u8string = basic_string, allocator>; #endif // defined(__cpp_lib_char8_t) _EXPORT_STD using u16string = basic_string, allocator>; _EXPORT_STD using u32string = basic_string, allocator>; template struct hash, _Alloc>> : _Conditionally_enabled_hash, _Alloc>, _Is_EcharT<_Elem>> { _NODISCARD static size_t _Do_hash(const basic_string<_Elem, char_traits<_Elem>, _Alloc>& _Keyval) noexcept { return _Hash_array_representation(_Keyval.c_str(), static_cast(_Keyval.size())); } }; _EXPORT_STD template basic_istream<_Elem, _Traits>& operator>>( basic_istream<_Elem, _Traits>& _Istr, basic_string<_Elem, _Traits, _Alloc>& _Str) { using _Myis = basic_istream<_Elem, _Traits>; using _Ctype = typename _Myis::_Ctype; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; using _Mysizt = typename _Mystr::size_type; typename _Myis::iostate _State = _Myis::goodbit; bool _Changed = false; const typename _Myis::sentry _Ok(_Istr); if (_Ok) { // state okay, extract characters const _Ctype& _Ctype_fac = _STD use_facet<_Ctype>(_Istr.getloc()); _Str.erase(); _TRY_IO_BEGIN _Mysizt _Size; if (0 < _Istr.width() && static_cast<_Mysizt>(_Istr.width()) < _Str.max_size()) { _Size = static_cast<_Mysizt>(_Istr.width()); } else { _Size = _Str.max_size(); } typename _Traits::int_type _Meta = _Istr.rdbuf()->sgetc(); for (; 0 < _Size; --_Size, _Meta = _Istr.rdbuf()->snextc()) { if (_Traits::eq_int_type(_Traits::eof(), _Meta)) { // end of file, quit _State |= _Myis::eofbit; break; } else if (_Ctype_fac.is(_Ctype::space, _Traits::to_char_type(_Meta))) { break; // whitespace, quit } else { // add character to string _Str.push_back(_Traits::to_char_type(_Meta)); _Changed = true; } } _CATCH_IO_(_Myis, _Istr) } _Istr.width(0); if (!_Changed) { _State |= _Myis::failbit; } _Istr.setstate(_State); return _Istr; } _EXPORT_STD template basic_ostream<_Elem, _Traits>& operator<<( basic_ostream<_Elem, _Traits>& _Ostr, const basic_string<_Elem, _Traits, _Alloc>& _Str) { return _Insert_string(_Ostr, _Str.data(), _Str.size()); } inline namespace literals { inline namespace string_literals { _EXPORT_STD _NODISCARD _CONSTEXPR20 string operator""s(const char* _Str, size_t _Len) { return string{_Str, _Len}; } _EXPORT_STD _NODISCARD _CONSTEXPR20 wstring operator""s(const wchar_t* _Str, size_t _Len) { return wstring{_Str, _Len}; } #ifdef __cpp_char8_t _EXPORT_STD _NODISCARD _CONSTEXPR20 basic_string operator""s(const char8_t* _Str, size_t _Len) { return basic_string{_Str, _Len}; } #endif // defined(__cpp_char8_t) _EXPORT_STD _NODISCARD _CONSTEXPR20 u16string operator""s(const char16_t* _Str, size_t _Len) { return u16string{_Str, _Len}; } _EXPORT_STD _NODISCARD _CONSTEXPR20 u32string operator""s(const char32_t* _Str, size_t _Len) { return u32string{_Str, _Len}; } } // namespace string_literals } // namespace literals #if _HAS_CXX20 _EXPORT_STD template constexpr basic_string<_Elem, _Traits, _Alloc>::size_type erase( basic_string<_Elem, _Traits, _Alloc>& _Cont, const _Uty& _Val) { return _STD _Erase_remove(_Cont, _Val); } _EXPORT_STD template constexpr basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( basic_string<_Elem, _Traits, _Alloc>& _Cont, _Pr _Pred) { return _STD _Erase_remove_if(_Cont, _STD _Pass_fn(_Pred)); } #endif // _HAS_CXX20 #if _HAS_CXX17 namespace pmr { _EXPORT_STD template > using basic_string = _STD basic_string<_Elem, _Traits, polymorphic_allocator<_Elem>>; _EXPORT_STD using string = basic_string; #ifdef __cpp_lib_char8_t _EXPORT_STD using u8string = basic_string; #endif // defined(__cpp_lib_char8_t) _EXPORT_STD using u16string = basic_string; _EXPORT_STD using u32string = basic_string; _EXPORT_STD using wstring = basic_string; } // namespace pmr #endif // _HAS_CXX17 #if _HAS_CXX23 template constexpr bool _Has_guaranteed_push_back> = true; #endif // _HAS_CXX23 _STD_END #undef _ASAN_STRING_REMOVE #undef _ASAN_STRING_CREATE #undef _ASAN_STRING_MODIFY #undef _INSERT_STRING_ANNOTATION #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) #endif // _STL_COMPILER_PREPROCESSOR #endif // _XSTRING_