From f9d1af0fbdd01e0cc8cea96cc6967d67aec2d108 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sun, 15 May 2022 17:43:12 -0700 Subject: [PATCH] GH-2196 --- stl/CMakeLists.txt | 4 +- stl/inc/sstream | 6 +- stl/inc/xstring | 720 +++++-- stl/msbuild/stl_base/libcp.settings.targets | 2 +- stl/src/asan.cpp | 1 + stl/src/asan_noop.cpp | 1 + tests/std/test.lst | 1 + .../GH_002030_asan_annotate_string/env.lst | 60 + .../GH_002030_asan_annotate_string/test.cpp | 1825 +++++++++++++++++ 9 files changed, 2408 insertions(+), 212 deletions(-) create mode 100644 tests/std/tests/GH_002030_asan_annotate_string/env.lst create mode 100644 tests/std/tests/GH_002030_asan_annotate_string/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 15915bd0f1..318279f3fb 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -545,11 +545,11 @@ add_stl_dlls("d" "_DEBUG" "${VCLIBS_DEBUG_OPTIONS}" "" "/opt:ref,noicf") function(add_stl_statics FLAVOR_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIONS) add_library(libcpmt${FLAVOR_SUFFIX}_eha OBJECT ${EHA_SOURCES}) - target_compile_definitions(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR") + target_compile_definitions(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR;_ANNOTATE_STRING") target_compile_options(libcpmt${FLAVOR_SUFFIX}_eha PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};/EHa") add_library(libcpmt${FLAVOR_SUFFIX} STATIC ${HEADERS} ${IMPLIB_SOURCES} ${SOURCES} ${INITIALIZER_SOURCES} ${STATIC_SOURCES}) - target_compile_definitions(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR") + target_compile_definitions(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_DEFINITIONS};_ANNOTATE_VECTOR;_ANNOTATE_STRING") target_compile_options(libcpmt${FLAVOR_SUFFIX} PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};/EHsc") target_link_libraries(libcpmt${FLAVOR_SUFFIX} PRIVATE Boost::math libcpmt${FLAVOR_SUFFIX}_eha) endfunction() diff --git a/stl/inc/sstream b/stl/inc/sstream index 7f10339866..f266a589dd 100644 --- a/stl/inc/sstream +++ b/stl/inc/sstream @@ -184,7 +184,6 @@ public: // the buffer is full _Result.assign(_View._Ptr, _View._Size); } else { - _Traits::assign(_View._Ptr[_View._Size], _Elem()); if (_Result._Move_assign_from_buffer(_View._Ptr, _View._Size, _View._Res)) { _Mystate &= ~_Allocated; } @@ -428,9 +427,8 @@ protected: _Xbad_alloc(); } - if (_Count != 0 - && (_State & (_Noread | _Constant)) - != (_Noread | _Constant)) { // finite buffer that can be read or written, set it up + if (_Count != 0 && (_State & (_Noread | _Constant)) != (_Noread | _Constant)) { + // finite buffer that can be read or written, set it up const auto _Pnew = _Unfancy(_Al.allocate(_Count)); _Traits::copy(_Pnew, _Ptr, _Count); _Seekhigh = _Pnew + _Count; diff --git a/stl/inc/xstring b/stl/inc/xstring index 91cdb517d1..b6e206b10f 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -2338,6 +2338,55 @@ struct _String_constructor_rvalue_allocator_tag { _Xlength_error("string too long"); } +#if !defined(_M_CEE_PURE) && !defined(_DISABLE_STRING_ANNOTATION) +#if defined(__SANITIZE_ADDRESS__) +#define _ACTIVATE_STRING_ANNOTATION +#define _INSERT_STRING_ANNOTATION +#elif defined(__clang__) && defined(__has_feature) // ^^^ __SANITIZE_ADDRESS__ ^^^ // vvv __clang__ vvv +#if __has_feature(address_sanitizer) +#define _ACTIVATE_STRING_ANNOTATION +#define _INSERT_STRING_ANNOTATION +#pragma comment(linker, "/INFERASANLIBS") +#endif // __has_feature(address_sanitizer) +#elif defined(_ANNOTATE_STRING) // ^^^ __clang__ ^^^ // vvv _ANNOTATE_STRING vvv +#define _INSERT_STRING_ANNOTATION +#endif // _ANNOTATE_STRING +#endif // !_M_CEE_PURE && !_DISABLE_STRING_ANNOTATION + +#ifdef _ACTIVATE_STRING_ANNOTATION +#pragma comment(lib, "stl_asan") +#pragma detect_mismatch("annotate_string", "1") +#endif // _ACTIVATE_STRING_ANNOTATION + +#ifdef _INSERT_STRING_ANNOTATION +extern "C" { +void __cdecl __sanitizer_annotate_contiguous_container( + const void* _First, const void* _End, const void* _Old_last, const void* _New_last) noexcept; +extern const bool _Asan_string_should_annotate; +} + +#if defined(_M_IX86) +#pragma comment(linker, \ + "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") +#elif defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64) +#pragma comment(linker, \ + "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") +#pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") +#endif + +template +_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment_string = alignof(typename _String::value_type) + >= _Asan_granularity; + +template +_INLINE_VAR constexpr bool _Has_minimum_allocation_alignment_string<_String, + void_t> = + _String::allocator_type::_Minimum_allocation_alignment >= _Asan_granularity; +#else // ^^^ _INSERT_STRING_ANNOTATION ^^^ // vvv !_INSERT_STRING_ANNOTATION vvv +#pragma detect_mismatch("annotate_string", "0") +#endif // !_INSERT_STRING_ANNOTATION + template , class _Alloc = allocator<_Elem>> class basic_string { // null-terminated transparent array of elements private: @@ -2410,23 +2459,102 @@ private: int>; #endif // _HAS_CXX17 -public: - _CONSTEXPR20 basic_string(const basic_string& _Right) - : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Construct_lv_contents(_Right); - _Proxy._Release(); +#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 basic_string(const basic_string& _Right, const _Alloc& _Al) - : _Mypair(_One_then_variadic_args_t{}, _Al) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Construct_lv_contents(_Right); - _Proxy._Release(); + _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 difference_type _Count) const noexcept { + // Extends/shrinks the annotated range by _Count + if (_Count == 0) { + return; + } + + auto& _My_data = _Mypair._Myval2; + _Apply_annotation( + _My_data._Myptr(), _My_data._Myres, _My_data._Mysize, static_cast(_My_data._Mysize + _Count)); } + _NODISCARD static const void* _Get_aligned_first(const void* _First, const size_type _Capacity) noexcept { + const char* _CFirst = reinterpret_cast(_First); + + if (_Capacity >= _Asan_granularity) { // We are guaranteed to have sufficient space to find an aligned address + return reinterpret_cast( + (reinterpret_cast(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1)); + } + + uintptr_t _Alignment_offset = reinterpret_cast(_CFirst) & (_Asan_granularity - 1); + if (_Alignment_offset != 0) { + _Alignment_offset = _Asan_granularity - _Alignment_offset; + } + + if (_Capacity > _Alignment_offset) { + return _CFirst + _Alignment_offset; + } + + return nullptr; + } + + static _CONSTEXPR20 void _Apply_annotation(const value_type* _Ptr, const size_type _Capacity, + const size_type _Old_size, const size_type _New_size) noexcept { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return; + } +#endif // _HAS_CXX20 + + if (!_Asan_string_should_annotate) { + return; + } + + // We need to check whether we have a misaligned SSO buffer because of the proxy in `_Container_base` (e.g. x86) + if constexpr (_Memcpy_val_offset % _Asan_granularity != 0) { + const uintptr_t _Alignment_offset = reinterpret_cast(_Ptr) & (_Asan_granularity - 1); + if (_Alignment_offset != 0 && _Capacity == _BUF_SIZE - 1) { + return; + } + } + + // Needs to consider the null terminator + const char* _First = reinterpret_cast(_Ptr); + const char* _End = reinterpret_cast(_Ptr + _Capacity + 1); + const char* _Old_last = reinterpret_cast(_Ptr + _Old_size + 1); + const char* _New_last = reinterpret_cast(_Ptr + _New_size + 1); + if constexpr (_Has_minimum_allocation_alignment_string) { + __sanitizer_annotate_contiguous_container(_First, _End, _Old_last, _New_last); + } else { + const void* _Aligned_first = _Get_aligned_first(_First, _Capacity + 1); + if (!_Aligned_first) { + // There is no aligned address within the underlying buffer. Nothing to do + return; + } + + const void* _Aligned_old_last = _Old_last < _Aligned_first ? _Aligned_first : _Old_last; + const void* _Aligned_new_last = _New_last < _Aligned_first ? _Aligned_first : _New_last; + const void* _Aligned_end = _End < _Aligned_first ? _Aligned_first : _End; + __sanitizer_annotate_contiguous_container( + _Aligned_first, _Aligned_end, _Aligned_old_last, _Aligned_new_last); + } + } + +#define _ASAN_STRING_MODIFY(n) _Modify_annotation((n)) +#define _ASAN_STRING_REMOVE(_Str) (_Str)._Remove_annotation() +#define _ASAN_STRING_CREATE(_Str) (_Str)._Create_annotation() +#else // ^^^ _INSERT_STRING_ANNOTATION ^^^ // vvv !_INSERT_STRING_ANNOTATION vvv +#define _ASAN_STRING_MODIFY(n) +#define _ASAN_STRING_REMOVE(_Str) +#define _ASAN_STRING_CREATE(_Str) +#endif // !_INSERT_STRING_ANNOTATION + +public: _CONSTEXPR20 basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); @@ -2438,50 +2566,44 @@ public: _Tidy_init(); } + _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, ) - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Right, _Roff, npos); - _Proxy._Release(); + _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) - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Right, _Roff, _Count); - _Proxy._Release(); + _Right._Mypair._Myval2._Check_offset(_Roff); + _Construct<_Construct_strategy::_From_ptr>( + _Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count)); } _CONSTEXPR20 basic_string(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) : _Mypair(_Zero_then_variadic_args_t{}) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Ptr, _Count); - _Proxy._Release(); + _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Count); } _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) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Ptr, _Count); - _Proxy._Release(); + _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Count); } _CONSTEXPR20 basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Ptr); - _Proxy._Release(); + _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Convert_size(_Traits::length(_Ptr))); } #if _HAS_CXX23 @@ -2493,21 +2615,12 @@ public: #endif // _HAS_CXX17 _CONSTEXPR20 basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Ptr); - _Proxy._Release(); + _Construct<_Construct_strategy::_From_ptr>(_Ptr, _Convert_size(_Traits::length(_Ptr))); } _CONSTEXPR20 basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) - : _Mypair(_Zero_then_variadic_args_t{}) { - // construct from _Count * _Ch - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Count, _Ch); - _Proxy._Release(); + : _Mypair(_Zero_then_variadic_args_t{}) { // construct from _Count * _Ch + _Construct<_Construct_strategy::_From_char>(_Ch, _Count); } #if _HAS_CXX17 @@ -2515,47 +2628,195 @@ public: #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 - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Count, _Ch); - _Proxy._Release(); + _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) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); - const auto _ULast = _Get_unwrapped(_Last); - - if constexpr (is_same_v>, _Elem>) { - if (_UFirst != _ULast) { - assign(_UFirst, _Convert_size(static_cast(_ULast - _UFirst))); - } + auto _UFirst = _Get_unwrapped(_First); + auto _ULast = _Get_unwrapped(_Last); + if (_UFirst == _ULast) { + _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); + _Tidy_init(); } else { - if constexpr (_Is_fwd_iter_v<_Iter>) { - const auto _Count = _Convert_size(static_cast(_STD distance(_UFirst, _ULast))); - reserve(_Count); + if constexpr (_Is_elem_cptr::value) { + _Construct<_Construct_strategy::_From_ptr>( + _UFirst, _Convert_size(static_cast(_ULast - _UFirst))); } else { - static_assert(_Is_input_iter_v<_Iter>, "Should be at least input iterator"); + _Construct_from_iter(_STD move(_UFirst), _STD move(_ULast)); + } + } + } + +private: + 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) { + 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_cptr<_Char_or_ptr>::value); + } + + if (_Count > max_size()) { + _Xlen_string(); // result too long + } + + auto& _My_data = _Mypair._Myval2; + auto& _Al = _Getal(); + auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al); + _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); + +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + _My_data._Myres = _BUF_SIZE; // TRANSITION: constexpr SSO + } + + const bool _Stay_small = _Count < _BUF_SIZE && !_STD is_constant_evaluated(); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + const bool _Stay_small = _Count < _BUF_SIZE; +#endif // _HAS_CXX20 + + if (_Stay_small) { +#if _HAS_CXX20 + // TRANSITION: This is currently unused until SSO support is merged + if (_STD is_constant_evaluated()) { + _Construct_in_place(_My_data._Bx); + } +#endif // _HAS_CXX20 + + _My_data._Mysize = _Count; + _My_data._Myres = _BUF_SIZE - 1; + if constexpr (_Strat == _Construct_strategy::_From_char) { + _Traits::assign(_My_data._Bx._Buf, _Count, _Arg); + _Traits::assign(_My_data._Bx._Buf[_Count], _Elem()); + } else if constexpr (_Strat == _Construct_strategy::_From_ptr) { + _Traits::move(_My_data._Bx._Buf, _Arg, _Count); + _Traits::assign(_My_data._Bx._Buf[_Count], _Elem()); + } else { // _Strat == _Construct_strategy::_From_string +#ifdef _INSERT_STRING_ANNOTATION + _Traits::move(_My_data._Bx._Buf, _Arg, _Count); +#else // ^^^ _INSERT_STRING_ANNOTATION ^^^ // vvv !_INSERT_STRING_ANNOTATION vvv + _Traits::move(_My_data._Bx._Buf, _Arg, _BUF_SIZE); +#endif // !_INSERT_STRING_ANNOTATION + } + + _ASAN_STRING_CREATE(*this); + _Proxy._Release(); + return; + } + + _My_data._Myres = _BUF_SIZE - 1; + const size_type _New_capacity = _Calculate_growth(_Count); + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); + +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // _HAS_CXX20 + + _My_data._Mysize = _Count; + _My_data._Myres = _New_capacity; + if constexpr (_Strat == _Construct_strategy::_From_char) { + _Traits::assign(_Unfancy(_New_ptr), _Count, _Arg); + _Traits::assign(_Unfancy(_New_ptr)[_Count], _Elem()); + } else if constexpr (_Strat == _Construct_strategy::_From_ptr) { + _Traits::copy(_Unfancy(_New_ptr), _Arg, _Count); + _Traits::assign(_Unfancy(_New_ptr)[_Count], _Elem()); + } else { // _Strat == _Construct_strategy::_From_string + _Traits::copy(_Unfancy(_New_ptr), _Arg, _Count + 1); + } + + _ASAN_STRING_CREATE(*this); + _Proxy._Release(); + } + + template + _CONSTEXPR20 void _Construct_from_iter(_Iter _First, const _Iter _Last) { + auto& _My_data = _Mypair._Myval2; + auto& _Al = _Getal(); + auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al); + _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); + + _My_data._Mysize = 0; + _My_data._Myres = _BUF_SIZE - 1; +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + _Construct_in_place(_My_data._Bx); + _My_data._Myres = _BUF_SIZE; // TRANSITION: constexpr SSO + } +#endif // _HAS_CXX20 + + if constexpr (_Is_fwd_iter_v<_Iter>) { + const auto _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); + if (_Count > max_size()) { + _Xlen_string(); // result too long } - // initialize from [_First, _Last), input iterators - _Tidy_deallocate_guard _Guard{this}; - for (; _UFirst != _ULast; ++_UFirst) { - push_back(*_UFirst); +#if _HAS_CXX20 + const bool _Become_large = _Count >= _BUF_SIZE || _STD is_constant_evaluated(); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + const bool _Become_large = _Count >= _BUF_SIZE; +#endif // _HAS_CXX20 + if (_Become_large) { + const size_type _New_capacity = _Calculate_growth(_Count); + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); + _My_data._Myres = _New_capacity; + +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // _HAS_CXX20 + } + } + + _Tidy_deallocate_guard _Guard{this}; + for (; _First != _Last; ++_First) { + if constexpr (!_Is_fwd_iter_v<_Iter>) { + if (_My_data._Mysize == _My_data._Myres) { // Need to grow + if (_My_data._Mysize == max_size()) { + _Xlen_string(); // result too long + } + + const auto _Old_ptr = _My_data._Myptr(); + const size_type _New_capacity = _Calculate_growth(_My_data._Mysize); + const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // _HAS_CXX20 + _Traits::copy(_Unfancy(_New_ptr), _Old_ptr, _My_data._Mysize); + if (_My_data._Myres >= _BUF_SIZE) { // Need to deallocate old storage + _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); + _My_data._Bx._Ptr = _New_ptr; + } else { + _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); + } + _My_data._Myres = _New_capacity; + } } - _Guard._Target = nullptr; + _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: _CONSTEXPR20 basic_string(basic_string&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); @@ -2565,18 +2826,16 @@ public: _CONSTEXPR20 basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( _Alty_traits::is_always_equal::value) // strengthened : _Mypair(_One_then_variadic_args_t{}, _Al) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); if constexpr (!_Alty_traits::is_always_equal::value) { if (_Getal() != _Right._Getal()) { - _Construct_lv_contents(_Right); - _Proxy._Release(); + _Construct<_Construct_strategy::_From_string>( + _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); return; } } + _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Take_contents(_Right); - _Proxy._Release(); } _CONSTEXPR20 basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, @@ -2607,19 +2866,20 @@ public: const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws _Ptr = _Unfancy(_Fancyptr); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); - } #if _HAS_CXX20 - if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB - _Traits::assign(_Ptr, _New_capacity + 1, _Elem()); - } + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Ptr, _New_capacity + 1, _Elem()); + } #endif // _HAS_CXX20 + } _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; _Traits::copy(_Ptr, _Left_ptr, _Left_size); _Traits::copy(_Ptr + static_cast(_Left_size), _Right_ptr, _Right_size); _Traits::assign(_Ptr[_New_size], _Elem()); + _ASAN_STRING_CREATE(*this); _Proxy._Release(); } @@ -2643,6 +2903,7 @@ public: _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block _Take_contents(_Left); const auto _Ptr = _My_data._Myptr(); + _ASAN_STRING_MODIFY(static_cast(_Right_size)); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1); _My_data._Mysize = _New_size; return; @@ -2663,6 +2924,7 @@ public: _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block _Take_contents(_Right); const auto _Ptr = _Unfancy(_My_data._Bx._Ptr); + _ASAN_STRING_MODIFY(static_cast(_Left_size)); _Traits::move(_Ptr + _Left_size, _Ptr, _Right_size + 1); _Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size); _My_data._Mysize = _New_size; @@ -2691,6 +2953,7 @@ public: const auto _Ptr = _Unfancy(_Fancyptr); _Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1); + _ASAN_STRING_CREATE(*this); _Proxy._Release(); } @@ -2698,23 +2961,17 @@ public: template = 0> _CONSTEXPR20 explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Right); - _Proxy._Release(); + 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 - auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); - _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); - _Tidy_init(); - assign(_Right, _Roff, _Count); - _Proxy._Release(); + : _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(_Roff, _Count); + _Construct<_Construct_strategy::_From_ptr>(_As_sub_view.data(), _Convert_size(_As_sub_view.size())); } #endif // _HAS_CXX17 @@ -2728,19 +2985,26 @@ public: _NODISCARD bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { // Move assign from a buffer, used exclusively by basic_stringbuf; returns _Large_string_engaged() - _Tidy_deallocate(); - pointer _Fancy_right = _Refancy(_Right); - auto& _My_data = _Mypair._Myval2; - _My_data._Mysize = _Size; - _My_data._Myres = _Res - 1; - if (_My_data._Large_string_engaged()) { - _Construct_in_place(_My_data._Bx._Ptr, _Fancy_right); - return true; + auto& _My_data = _Mypair._Myval2; + _STL_INTERNAL_CHECK(!_My_data._Large_string_engaged() && _My_data._Mysize == 0); + _STL_INTERNAL_CHECK(_Size < _Res); // So there is room for null terminator + _Traits::assign(_Right[_Size], _Elem()); + + const bool _Is_large = _Res > _BUF_SIZE; // Note: _BUF_SIZE because _Res now includes the null terminator + if (_Is_large) { + _ASAN_STRING_REMOVE(*this); + _Construct_in_place(_My_data._Bx._Ptr, _Refancy(_Right)); + _My_data._Mysize = _Size; + _My_data._Myres = _Res - 1; + _ASAN_STRING_CREATE(*this); } else { + _ASAN_STRING_MODIFY(static_cast(_Size)); _Traits::copy(_My_data._Bx._Buf, _Right, _Res); - _My_data._Myres = _BUF_SIZE - 1; - return false; + _My_data._Mysize = _Size; + _My_data._Myres = _BUF_SIZE - 1; } + + return _Is_large; } // No instance of this type can exist where an exception may be thrown. @@ -2755,6 +3019,7 @@ public: _Released_buffer _Result; auto& _My_data = _Mypair._Myval2; _Result._Size = _My_data._Mysize; + _ASAN_STRING_REMOVE(*this); if (_My_data._Large_string_engaged()) { _Result._Ptr = _My_data._Bx._Ptr; _Result._Res = _My_data._Myres + 1; @@ -2836,7 +3101,21 @@ private: } #endif // _ITERATOR_DEBUG_LEVEL != 0 +#ifdef _INSERT_STRING_ANNOTATION + if (!_Right_data._Large_string_engaged()) { + _ASAN_STRING_REMOVE(_Right); + } +#endif // _INSERT_STRING_ANNOTATION + _Memcpy_val_from(_Right); + +#ifdef _INSERT_STRING_ANNOTATION + if (!_Right_data._Large_string_engaged()) { + _ASAN_STRING_REMOVE(_Right); + _ASAN_STRING_CREATE(*this); + } +#endif // _INSERT_STRING_ANNOTATION + _Right._Tidy_init(); return; } @@ -2847,6 +3126,11 @@ private: _Right_data._Bx._Ptr = nullptr; _Swap_proxy_and_iterators(_Right); } else { // copy small string buffer +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { // begin the lifetime of the array elements before copying into them + _Construct_in_place(_Mypair._Myval2._Bx); + } +#endif // _HAS_CXX20 _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); _Right_data._Orphan_all(); } @@ -2856,46 +3140,6 @@ private: _Right._Tidy_init(); } - _CONSTEXPR20 void _Construct_lv_contents(const basic_string& _Right) { - // assign by copying data stored in _Right - // pre: this != &_Right - // pre: *this owns no memory, iterators orphaned (note: - // _Buf/_Ptr/_Mysize/_Myres may be garbage init) - auto& _Right_data = _Right._Mypair._Myval2; - const size_type _Right_size = _Right_data._Mysize; - const _Elem* const _Right_ptr = _Right_data._Myptr(); - auto& _My_data = _Mypair._Myval2; - -#if _HAS_CXX20 - const bool _Stay_small = _Right_size < _BUF_SIZE && !_STD is_constant_evaluated(); -#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv - const bool _Stay_small = _Right_size < _BUF_SIZE; -#endif // _HAS_CXX20 - - // NOTE: even if _Right is in large mode, we only go into large mode ourselves if the actual size of _Right - // requires it - if (_Stay_small) { // stay small, don't allocate - _Traits::copy(_My_data._Bx._Buf, _Right_ptr, _BUF_SIZE); - _My_data._Mysize = _Right_size; - _My_data._Myres = _BUF_SIZE - 1; - return; - } - - auto& _Al = _Getal(); - const size_type _New_capacity = (_STD min)(_Right_size | _ALLOC_MASK, max_size()); - const pointer _New_array = _Al.allocate(_New_capacity + 1); // throws - _Construct_in_place(_My_data._Bx._Ptr, _New_array); - -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB - _Traits::assign(_Unfancy(_New_array), _New_capacity + 1, _Elem()); - } -#endif // _HAS_CXX20 - _Traits::copy(_Unfancy(_New_array), _Right_ptr, _Right_size + 1); - _My_data._Mysize = _Right_size; - _My_data._Myres = _New_capacity; - } - public: _CONSTEXPR20 basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) : _Mypair(_One_then_variadic_args_t{}, _Al) { @@ -2956,22 +3200,28 @@ public: static constexpr auto npos{static_cast(-1)}; private: - void _Copy_assign_val_from_small(const basic_string& _Right) { + _CONSTEXPR20 void _Copy_assign_val_from_small(const basic_string& _Right) { // TRANSITION, VSO-761321; inline into only caller when that's fixed #if _HAS_CXX20 _STL_ASSERT(!_STD is_constant_evaluated(), "SSO should be disabled in a constexpr context"); #endif // _HAS_CXX20 _Tidy_deallocate(); if constexpr (_Can_memcpy_val) { - _Memcpy_val_from(_Right); - } else { - auto& _My_data = _Mypair._Myval2; - auto& _Right_data = _Right._Mypair._Myval2; - - _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); - _My_data._Mysize = _Right_data._Mysize; - _My_data._Myres = _Right_data._Myres; +#if _HAS_CXX20 + if (!_STD is_constant_evaluated()) +#endif // _HAS_CXX20 + { + _Memcpy_val_from(_Right); + return; + } } + + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); + _My_data._Mysize = _Right_data._Mysize; + _My_data._Myres = _Right_data._Myres; } public: @@ -3036,6 +3286,7 @@ public: #endif // _HAS_CXX23 _CONSTEXPR20 basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} + _ASAN_STRING_MODIFY(static_cast(1 - _Mypair._Myval2._Mysize)); _Mypair._Myval2._Mysize = 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[0], _Ch); @@ -3095,6 +3346,7 @@ public: // append [_Ptr, _Ptr + _Count) const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { + _ASAN_STRING_MODIFY(static_cast(_Count)); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Traits::move(_Old_ptr + _Old_size, _Ptr, _Count); @@ -3121,6 +3373,7 @@ public: // append _Count * _Ch const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { + _ASAN_STRING_MODIFY(static_cast(_Count)); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Old_ptr + _Old_size, _Count, _Ch); @@ -3185,6 +3438,7 @@ public: _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) if (_Count <= _Mypair._Myval2._Myres) { + _ASAN_STRING_MODIFY(static_cast(_Count - _Mypair._Myval2._Mysize)); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; _Traits::move(_Old_ptr, _Ptr, _Count); @@ -3208,6 +3462,7 @@ public: _CONSTEXPR20 basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // assign _Count * _Ch if (_Count <= _Mypair._Myval2._Myres) { + _ASAN_STRING_MODIFY(static_cast(_Count - _Mypair._Myval2._Mysize)); _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; _Traits::assign(_Old_ptr, _Count, _Ch); @@ -3288,6 +3543,7 @@ public: #endif // _HAS_CXX20 if (_Check_overlap) { + _ASAN_STRING_MODIFY(static_cast(_Count)); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; @@ -3332,6 +3588,7 @@ public: _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { + _ASAN_STRING_MODIFY(static_cast(_Count)); _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; @@ -3404,8 +3661,9 @@ private: _Elem* const _My_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Erase_at = _My_ptr + _Off; const size_type _New_size = _Old_size - _Count; - _Mypair._Myval2._Mysize = _New_size; _Traits::move(_Erase_at, _Erase_at + _Count, _New_size - _Off + 1); // move suffix + null up + _ASAN_STRING_MODIFY(-static_cast(_Count)); + _Mypair._Myval2._Mysize = _New_size; return *this; } @@ -3725,18 +3983,18 @@ public: _CONSTEXPR20 void shrink_to_fit() { // reduce capacity auto& _My_data = _Mypair._Myval2; + if (!_My_data._Large_string_engaged()) { // can't shrink from small mode + return; + } + #if _HAS_CXX20 - if (!_STD is_constant_evaluated()) + const bool _Do_become_small = _My_data._Mysize < _BUF_SIZE && !_STD is_constant_evaluated(); +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + const bool _Do_become_small = _My_data._Mysize < _BUF_SIZE; #endif // _HAS_CXX20 - { - if (!_My_data._Large_string_engaged()) { // can't shrink from small mode - return; - } - - if (_My_data._Mysize < _BUF_SIZE) { - _Become_small(); - return; - } + if (_Do_become_small) { + _Become_small(); + return; } size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); @@ -3748,6 +4006,7 @@ public: if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws + _ASAN_STRING_REMOVE(*this); #if _HAS_CXX20 if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB @@ -3760,6 +4019,7 @@ public: _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); _My_data._Bx._Ptr = _New_ptr; _My_data._Myres = _Target_capacity; + _ASAN_STRING_CREATE(*this); } } @@ -3798,6 +4058,7 @@ public: _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) { + _ASAN_STRING_MODIFY(1); _Mypair._Myval2._Mysize = _Old_size + 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[_Old_size], _Ch); @@ -3995,12 +4256,15 @@ public: return _Count; } - void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) { + 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 - // (not _CONSTEXPR20; SSO should be disabled in a constexpr context) - const pointer _Ptr = _Starts_large._Bx._Ptr; _Destroy_in_place(_Starts_large._Bx._Ptr); +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + _Construct_in_place(_Starts_large._Bx); + } +#endif // _HAS_CXX20 _Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE); _Construct_in_place(_Starts_small._Bx._Ptr, _Ptr); } @@ -4009,6 +4273,21 @@ public: auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; + const bool _My_large = _My_data._Large_string_engaged(); + const bool _Right_large = _Right_data._Large_string_engaged(); +#ifdef _INSERT_STRING_ANNOTATION + if (_My_large && _Right_large) { + // nothing + } else if (_My_large) { + _ASAN_STRING_REMOVE(_Right); + } else if (_Right_large) { + _ASAN_STRING_REMOVE(*this); + } else { + _ASAN_STRING_REMOVE(_Right); + _ASAN_STRING_REMOVE(*this); + } +#endif // _INSERT_STRING_ANNOTATION + if constexpr (_Can_memcpy_val) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) @@ -4022,27 +4301,39 @@ public: _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); + +#ifdef _INSERT_STRING_ANNOTATION + if (_My_large && _Right_large) { + // nothing + } else if (_My_large) { + _ASAN_STRING_CREATE(*this); + } else if (_Right_large) { + _ASAN_STRING_CREATE(_Right); + } else { + _ASAN_STRING_CREATE(_Right); + _ASAN_STRING_CREATE(*this); + } +#endif // _INSERT_STRING_ANNOTATION + return; } } - const bool _My_large = _My_data._Large_string_engaged(); - const bool _Right_large = _Right_data._Large_string_engaged(); - if (_My_large) { - if (_Right_large) { // swap buffers, iterators preserved - _Swap_adl(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); - } else { // swap large with small - _Swap_bx_large_with_small(_My_data, _Right_data); - } + if (_My_large && _Right_large) { // swap buffers, iterators preserved + _Swap_adl(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); + } else if (_My_large) { // swap large with small + _Swap_bx_large_with_small(_My_data, _Right_data); + _ASAN_STRING_CREATE(*this); + } else if (_Right_large) { // swap small with large + _Swap_bx_large_with_small(_Right_data, _My_data); + _ASAN_STRING_CREATE(_Right); } 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, _BUF_SIZE); - _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _BUF_SIZE); - _Traits::copy(_Right_data._Bx._Buf, _Temp_buf, _BUF_SIZE); - } + _Elem _Temp_buf[_BUF_SIZE]; + _Traits::copy(_Temp_buf, _My_data._Bx._Buf, _My_data._Mysize + 1); + _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); + _Traits::copy(_Right_data._Bx._Buf, _Temp_buf, _My_data._Mysize + 1); + _ASAN_STRING_CREATE(_Right); + _ASAN_STRING_CREATE(*this); } _STD swap(_My_data._Mysize, _Right_data._Mysize); @@ -4476,6 +4767,7 @@ private: } #endif // _HAS_CXX20 _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...); @@ -4486,6 +4778,7 @@ private: _Construct_in_place(_Mypair._Myval2._Bx._Ptr, _New_ptr); } + _ASAN_STRING_CREATE(*this); return *this; } @@ -4511,6 +4804,7 @@ private: } #endif // _HAS_CXX20 _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); @@ -4524,25 +4818,34 @@ private: _Construct_in_place(_My_data._Bx._Ptr, _New_ptr); } + _ASAN_STRING_CREATE(*this); return *this; } - void _Become_small() { + _CONSTEXPR20 void _Become_small() { // release any held storage and return to small string mode - // pre: *this is in large string mode - // pre: this is small enough to return to small string mode - // (not _CONSTEXPR20; SSO should be disabled in a constexpr context) + auto& _My_data = _Mypair._Myval2; + _STL_INTERNAL_CHECK(_My_data._Large_string_engaged()); + _STL_INTERNAL_CHECK(_My_data._Mysize < _BUF_SIZE); - _Mypair._Myval2._Orphan_all(); - const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; + _My_data._Orphan_all(); + _ASAN_STRING_REMOVE(*this); + const pointer _Ptr = _My_data._Bx._Ptr; auto& _Al = _Getal(); - _Destroy_in_place(_Mypair._Myval2._Bx._Ptr); - _Traits::copy(_Mypair._Myval2._Bx._Buf, _Unfancy(_Ptr), _Mypair._Myval2._Mysize + 1); - _Al.deallocate(_Ptr, _Mypair._Myval2._Myres + 1); - _Mypair._Myval2._Myres = _BUF_SIZE - 1; + _Destroy_in_place(_My_data._Bx._Ptr); +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { // begin the lifetime of the array elements before copying into them + _Construct_in_place(_Mypair._Myval2._Bx); + } +#endif // _HAS_CXX20 + _Traits::copy(_My_data._Bx._Buf, _Unfancy(_Ptr), _My_data._Mysize + 1); + _Al.deallocate(_Ptr, _My_data._Myres + 1); + _My_data._Myres = _BUF_SIZE - 1; + _ASAN_STRING_CREATE(*this); } _CONSTEXPR20 void _Eos(const size_type _New_size) { // set new length and null terminator + _ASAN_STRING_MODIFY(static_cast(_New_size - _Mypair._Myval2._Mysize)); _Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _New_size], _Elem()); } @@ -4566,29 +4869,36 @@ private: // 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()); } + + _ASAN_STRING_CREATE(*this); } _CONSTEXPR20 void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage - _Mypair._Myval2._Orphan_all(); - if (_Mypair._Myval2._Large_string_engaged()) { - const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; + auto& _My_data = _Mypair._Myval2; + _My_data._Orphan_all(); + _ASAN_STRING_REMOVE(*this); + if (_My_data._Large_string_engaged()) { + const pointer _Ptr = _My_data._Bx._Ptr; auto& _Al = _Getal(); - _Destroy_in_place(_Mypair._Myval2._Bx._Ptr); - _Al.deallocate(_Ptr, _Mypair._Myval2._Myres + 1); + _Destroy_in_place(_My_data._Bx._Ptr); +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { // begin the lifetime of the array elements before copying into them + _Construct_in_place(_My_data._Bx); + } +#endif // _HAS_CXX20 + _Al.deallocate(_Ptr, _My_data._Myres + 1); } + _My_data._Mysize = 0; #if _HAS_CXX20 if (_STD is_constant_evaluated()) { - _Mypair._Myval2._Bx._Ptr = nullptr; - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = 0; + _My_data._Myres = 0; } else #endif // _HAS_CXX20 { - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; + _My_data._Myres = _BUF_SIZE - 1; // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + _Traits::assign(_My_data._Bx._Buf[0], _Elem()); } } diff --git a/stl/msbuild/stl_base/libcp.settings.targets b/stl/msbuild/stl_base/libcp.settings.targets index b373b39e2a..516e62b129 100644 --- a/stl/msbuild/stl_base/libcp.settings.targets +++ b/stl/msbuild/stl_base/libcp.settings.targets @@ -27,7 +27,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(OutputLibPdbPath)$(OutputName)$(PdbVerName).pdb $(ClDefines);_STL_CONCRT_SUPPORT - $(ClDefines);_VCRT_ALLOW_INTERNALS;_ANNOTATE_VECTOR + $(ClDefines);_VCRT_ALLOW_INTERNALS;_ANNOTATE_VECTOR;_ANNOTATE_STRING diff --git a/stl/src/asan.cpp b/stl/src/asan.cpp index b0068d91f7..a892af3014 100644 --- a/stl/src/asan.cpp +++ b/stl/src/asan.cpp @@ -3,6 +3,7 @@ namespace std { extern "C" { + extern const bool _Asan_string_should_annotate = true; extern const bool _Asan_vector_should_annotate = true; } } // namespace std diff --git a/stl/src/asan_noop.cpp b/stl/src/asan_noop.cpp index 46b3abf22f..d9b3b1d25b 100644 --- a/stl/src/asan_noop.cpp +++ b/stl/src/asan_noop.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception extern "C" { +extern const bool _Asan_string_should_annotate_default = false; extern const bool _Asan_vector_should_annotate_default = false; void __cdecl __sanitizer_annotate_contiguous_container_default( diff --git a/tests/std/test.lst b/tests/std/test.lst index 284fea8bc4..b266409747 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -187,6 +187,7 @@ tests\GH_001850_clog_tied_to_cout tests\GH_001858_iostream_exception tests\GH_001914_cached_position tests\GH_001964_constexpr_generate_canonical +tests\GH_002030_asan_annotate_string tests\GH_002030_asan_annotate_vector tests\GH_002039_byte_is_not_trivially_swappable tests\GH_002058_debug_iterator_race diff --git a/tests/std/tests/GH_002030_asan_annotate_string/env.lst b/tests/std/tests/GH_002030_asan_annotate_string/env.lst new file mode 100644 index 0000000000..413875d9c5 --- /dev/null +++ b/tests/std/tests/GH_002030_asan_annotate_string/env.lst @@ -0,0 +1,60 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This test matrix is the usual test matrix, with all currently unsupported options removed, crossed with the ASan flags. +# TRANSITION, VSO-1350252 +# Due to a bug in the ASan libs using ASan with /MD or /MT requires IDL==0 and using /MDd or /MTd requires IDL==2. +# clang-cl does not currently support targeting /MDd or /MTd. +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" +RUNALL_CROSSLIST +PM_CL="-fsanitize=address /BE /c /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /BE /c /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /BE /c /EHsc /MT /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /BE /c /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MD /std:c++17 /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MD /std:c++20 /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fp:strict /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /Za /EHsc /MD /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="-fsanitize=address /Za /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MT /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /BE /c /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++14 /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++17 /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++20 /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++14 /fp:except /Zc:preprocessor /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++17 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++20 /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MT /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive- /fp:strict /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /EHsc /MTd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /Za /EHsc /MD /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +PM_CL="/D_ANNOTATE_STRING /Za /EHsc /MDd /std:c++latest /permissive- /fno-sanitize-address-vcasan-lib" +# TRANSITION, clang-cl does not support /alternatename so we cannot test /D_ANNOTATE_STRING without -fsanitize=address +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++14" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MT /std:c++latest /permissive- /D_HAS_CXX23 /fp:strict" diff --git a/tests/std/tests/GH_002030_asan_annotate_string/test.cpp b/tests/std/tests/GH_002030_asan_annotate_string/test.cpp new file mode 100644 index 0000000000..7c9bea3e9c --- /dev/null +++ b/tests/std/tests/GH_002030_asan_annotate_string/test.cpp @@ -0,0 +1,1825 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// REQUIRES: asan, x64 || x86 + +#pragma warning(disable : 4389) // signed/unsigned mismatch in arithmetic +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension +#pragma warning(disable : 6326) // Potential comparison of a constant with another constant. + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#if _HAS_CXX17 +#include +#endif // _HAS_CXX17 +#include +#include + +using namespace std; + +extern "C" int __sanitizer_verify_contiguous_container(const void* beg, const void* mid, const void* end) noexcept; + +constexpr auto literal_input = "Hello fluffy kittens"; +#ifdef __cpp_char8_t +constexpr auto literal_input_u8 = u8"Hello fluffy kittens"; +#endif // __cpp_char8_t +constexpr auto literal_input_u16 = u"Hello fluffy kittens"; +constexpr auto literal_input_u32 = U"Hello fluffy kittens"; +constexpr auto literal_input_w = L"Hello fluffy kittens"; + +template +constexpr auto get_large_input() { + if constexpr (is_same_v) { + return literal_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u16; + } else if constexpr (is_same_v) { + return literal_input_u32; + } else { + return literal_input_w; + } +} + +template +constexpr auto get_sso_input() { + if constexpr (is_same_v) { + return "cat"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"cat"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"cat"; + } else if constexpr (is_same_v) { + return U"cat"; + } else { + return L"cat"; + } +} + +#if _HAS_CXX17 +template +constexpr auto get_large_input_view() { + return basic_string_view{get_large_input()}; +} + +template +constexpr auto get_sso_input_view() { + return basic_string_view{get_sso_input()}; +} +#endif // _HAS_CXX17 + +#if _HAS_CXX17 +template +struct string_view_convertible { + constexpr operator basic_string_view() const noexcept { + return get_large_input_view(); + } +}; + +template +struct string_view_convertible_sso { + constexpr operator basic_string_view() const noexcept { + return get_sso_input_view(); + } +}; +#endif // _HAS_CXX17 + +template +struct throw_on_conversion { + throw_on_conversion() = default; + throw_on_conversion(CharType) {} + operator const CharType() const { + throw 42; + } +}; + +template +class input_iterator_tester { +private: + CharType data[N] = {}; + +public: + input_iterator_tester() noexcept { + fill(data, data + N, CharType{'b'}); + } + + class iterator { + private: + CharType* curr; + + public: + using iterator_category = input_iterator_tag; + using value_type = CharType; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = CharType&; + + explicit iterator(CharType* start) : curr(start) {} + + reference operator*() const { + return *curr; + } + + iterator& operator++() { + ++curr; + return *this; + } + + iterator operator++(int) { + auto tmp = *this; + ++curr; + return tmp; + } + + bool operator==(const iterator& that) const { + return curr == that.curr; + } + + bool operator!=(const iterator& that) const { + return !(*this == that); + } + }; + + iterator begin() { + return iterator(data); + } + + iterator end() { + return iterator(data + N); + } +}; + +template +bool verify_string(basic_string, Alloc>& str) { + constexpr auto proxy_size = _Size_after_ebco_v<_Container_base>; + if constexpr (proxy_size % _Asan_granularity != 0) { // If we have a misaligned SSO buffer we disable ASAN + constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; + if (str.capacity() == max_sso_size) { + return true; + } + } + + size_t buffer_size = (str.capacity() + 1) * sizeof(CharType); + void* buffer = const_cast(static_cast(str.data())); + void* aligned_start = align(8, 1, buffer, buffer_size); + + if (!aligned_start) { + return true; + } + + const void* end = const_cast(static_cast(str.data() + (str.capacity() + 1))); + const void* mid = const_cast(static_cast(str.data() + str.size() + 1)); + const void* aligned_mid = mid > aligned_start ? mid : aligned_start; + + return __sanitizer_verify_contiguous_container(aligned_start, aligned_mid, end) != 0; +} + +// Note: This class does not satisfy all the allocator requirements but is sufficient for this test. +template +struct custom_test_allocator { + using value_type = CharType; + using propagate_on_container_move_assignment = Pocma; + using is_always_equal = Stateless; +}; + +template +constexpr bool operator==( + const custom_test_allocator&, const custom_test_allocator&) noexcept { + return Stateless::value; +} + +template +constexpr bool operator!=( + const custom_test_allocator&, const custom_test_allocator&) noexcept { + return !Stateless::value; +} + +template +struct aligned_allocator : public custom_test_allocator { + static constexpr size_t _Minimum_allocation_alignment = 8; + + aligned_allocator() = default; + template + constexpr aligned_allocator(const aligned_allocator&) noexcept {} + + CharType* allocate(size_t n) { + return new CharType[n]; + } + + void deallocate(CharType* p, size_t) noexcept { + delete[] p; + } +}; + +template +struct explicit_allocator : public custom_test_allocator { + static constexpr size_t _Minimum_allocation_alignment = alignof(CharType); + + explicit_allocator() = default; + template + constexpr explicit_allocator(const explicit_allocator&) noexcept {} + + CharType* allocate(size_t n) { + CharType* mem = new CharType[n + 1]; + return mem + 1; + } + + void deallocate(CharType* p, size_t) noexcept { + delete[](p - 1); + } +}; + +template +struct implicit_allocator : public custom_test_allocator { + implicit_allocator() = default; + template + constexpr implicit_allocator(const implicit_allocator&) noexcept {} + + CharType* allocate(size_t n) { + CharType* mem = new CharType[n + 1]; + return mem + 1; + } + + void deallocate(CharType* p, size_t) noexcept { + delete[](p - 1); + } +}; + +template +void test_construction() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + { // constructors + // range constructors + str literal_constructed{get_large_input()}; + assert(verify_string(literal_constructed)); + + str literal_constructed_sso{get_sso_input()}; + assert(verify_string(literal_constructed_sso)); + + str initializer_list_constructed({CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, CharType{'o'}, + CharType{' '}, // + CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, + CharType{'k'}, CharType{'i'}, CharType{'t'}, CharType{'t'}, CharType{'e'}, CharType{'n'}, CharType{'s'}}); + assert(verify_string(initializer_list_constructed)); + + str initializer_list_constructed_sso({CharType{'c'}, CharType{'a'}, CharType{'t'}}); + assert(verify_string(initializer_list_constructed_sso)); + + // special member functions + str default_constructed; + assert(verify_string(default_constructed)); + + str copy_constructed(literal_constructed); + assert(verify_string(copy_constructed)); + + str copy_constructed_sso(literal_constructed_sso); + assert(verify_string(copy_constructed_sso)); + + str move_constructed(move(copy_constructed)); + assert(verify_string(copy_constructed)); + assert(verify_string(move_constructed)); + + str move_constructed_sso(move(copy_constructed_sso)); + assert(verify_string(copy_constructed_sso)); + assert(verify_string(move_constructed_sso)); + + str copy_assigned_sso_to_sso(get_sso_input()); + copy_assigned_sso_to_sso = literal_constructed_sso; + assert(verify_string(literal_constructed_sso)); + assert(verify_string(copy_assigned_sso_to_sso)); + + str copy_assigned_large_to_sso(get_sso_input()); + copy_assigned_large_to_sso = literal_constructed; + assert(verify_string(literal_constructed)); + assert(verify_string(copy_assigned_large_to_sso)); + + str copy_assigned_sso_to_large(get_large_input()); + copy_assigned_sso_to_large = literal_constructed_sso; + assert(verify_string(literal_constructed_sso)); + assert(verify_string(copy_assigned_sso_to_large)); + + str copy_assigned_large_to_large(get_large_input()); + copy_assigned_large_to_large = literal_constructed; + assert(verify_string(literal_constructed)); + assert(verify_string(copy_assigned_large_to_large)); + + str move_assigned_sso_to_sso(get_sso_input()); + move_assigned_sso_to_sso = move(copy_assigned_sso_to_sso); + assert(verify_string(copy_assigned_sso_to_sso)); + assert(verify_string(move_assigned_sso_to_sso)); + + str move_assigned_large_to_sso(get_sso_input()); + move_assigned_large_to_sso = move(copy_assigned_large_to_sso); + assert(verify_string(copy_assigned_large_to_sso)); + assert(verify_string(move_assigned_large_to_sso)); + + str move_assigned_sso_to_large(get_large_input()); + move_assigned_sso_to_large = move(copy_assigned_sso_to_large); + assert(verify_string(copy_assigned_sso_to_large)); + assert(verify_string(move_assigned_sso_to_large)); + + str move_assigned_large_to_large(get_large_input()); + move_assigned_large_to_large = move(copy_assigned_large_to_large); + assert(verify_string(copy_assigned_large_to_large)); + assert(verify_string(move_assigned_large_to_large)); + + // Other constructors + str size_value_constructed(20, CharType{'a'}); + assert(verify_string(size_value_constructed)); + + str size_value_constructed_sso(2, CharType{'a'}); + assert(verify_string(size_value_constructed_sso)); + + str ptr_size_constructed(get_large_input(), 20); + assert(verify_string(ptr_size_constructed)); + + str ptr_size_constructed_sso(get_large_input(), 2); + assert(verify_string(ptr_size_constructed_sso)); + + str iterator_constructed(literal_constructed.begin(), literal_constructed.end()); + assert(verify_string(iterator_constructed)); + assert(verify_string(literal_constructed)); + + str iterator_constructed_sso(literal_constructed_sso.begin(), literal_constructed_sso.end()); + assert(verify_string(iterator_constructed_sso)); + assert(verify_string(literal_constructed_sso)); + + input_iterator_tester input_iter_data; + str input_iterator_constructed(input_iter_data.begin(), input_iter_data.end()); + assert(verify_string(input_iterator_constructed)); + + input_iterator_tester input_iter_data_sso; + str input_iterator_constructed_sso(input_iter_data_sso.begin(), input_iter_data_sso.end()); + assert(verify_string(input_iterator_constructed_sso)); + +#if _HAS_CXX17 + str copy_start_constructed_large_to_large(literal_constructed, 2); + assert(verify_string(copy_start_constructed_large_to_large)); + assert(verify_string(literal_constructed)); + + str copy_start_constructed_large_to_sso(literal_constructed, 20); + assert(verify_string(copy_start_constructed_large_to_sso)); + assert(verify_string(literal_constructed)); + + str copy_start_constructed_sso_to_sso(literal_constructed_sso, 1); + assert(verify_string(copy_start_constructed_sso_to_sso)); + assert(verify_string(literal_constructed_sso)); + + str copy_start_length_constructed_large_to_large(literal_constructed, 2, 18); + assert(verify_string(copy_start_length_constructed_large_to_large)); + assert(verify_string(literal_constructed)); + + str copy_start_length_constructed_large_to_sso(literal_constructed, 20, 2); + assert(verify_string(copy_start_length_constructed_large_to_sso)); + assert(verify_string(literal_constructed)); + + str copy_start_length_constructed_sso_to_sso(literal_constructed_sso, 1, 2); + assert(verify_string(copy_start_length_constructed_sso_to_sso)); + assert(verify_string(literal_constructed_sso)); + + str view_constructed(get_large_input_view()); + assert(verify_string(view_constructed)); + + str view_constructed_sso(get_sso_input_view()); + assert(verify_string(view_constructed_sso)); + + const string_view_convertible convertible; + str conversion_constructed(convertible); + assert(verify_string(conversion_constructed)); + + str conversion_start_length_constructed(convertible, 2, 18); + assert(verify_string(conversion_start_length_constructed)); + + str conversion_start_length_constructed_sso(convertible, 18, 2); + assert(verify_string(conversion_start_length_constructed)); + + const string_view_convertible_sso convertible_sso; + str conversion_constructed_sso(convertible_sso); + assert(verify_string(conversion_constructed_sso)); + + str conversion_start_length_constructed_sso_to_sso(convertible_sso, 1, 2); + assert(verify_string(conversion_start_length_constructed_sso_to_sso)); +#endif // _HAS_CXX17 + } + + { // allocator constructors + Alloc alloc; + + // range constructors + str literal_constructed{get_large_input(), alloc}; + assert(verify_string(literal_constructed)); + + str literal_constructed_sso{get_sso_input(), alloc}; + assert(verify_string(literal_constructed_sso)); + + str initializer_list_constructed( + {CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, CharType{'o'}, CharType{' '}, // + CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, + CharType{'k'}, CharType{'i'}, CharType{'t'}, CharType{'t'}, CharType{'e'}, CharType{'n'}, + CharType{'s'}}, + alloc); + assert(verify_string(initializer_list_constructed)); + + str initializer_list_constructed_sso({CharType{'c'}, CharType{'a'}, CharType{'t'}}, alloc); + assert(verify_string(initializer_list_constructed_sso)); + + // special member functions + str default_constructed; + assert(verify_string(default_constructed)); + + str copy_constructed(literal_constructed); + assert(verify_string(copy_constructed)); + + str copy_constructed_sso(literal_constructed_sso); + assert(verify_string(copy_constructed_sso)); + + str move_constructed(move(copy_constructed)); + assert(verify_string(copy_constructed)); + assert(verify_string(move_constructed)); + + str move_constructed_sso(move(copy_constructed_sso)); + assert(verify_string(copy_constructed_sso)); + assert(verify_string(move_constructed_sso)); + + // Other constructors + str size_value_constructed(20, CharType{'a'}, alloc); + assert(verify_string(size_value_constructed)); + + str size_value_constructed_sso(2, CharType{'a'}, alloc); + assert(verify_string(size_value_constructed_sso)); + + str ptr_size_constructed(get_large_input(), 20, alloc); + assert(verify_string(ptr_size_constructed)); + + str ptr_size_constructed_sso(get_large_input(), 2, alloc); + assert(verify_string(ptr_size_constructed_sso)); + + str iterator_constructed(literal_constructed.begin(), literal_constructed.end(), alloc); + assert(verify_string(iterator_constructed)); + assert(verify_string(literal_constructed)); + + str iterator_constructed_sso(literal_constructed_sso.begin(), literal_constructed_sso.end(), alloc); + assert(verify_string(iterator_constructed_sso)); + assert(verify_string(literal_constructed_sso)); + + input_iterator_tester input_iter_data; + str input_iterator_constructed(input_iter_data.begin(), input_iter_data.end(), alloc); + assert(verify_string(input_iterator_constructed)); + + input_iterator_tester input_iter_data_sso; + str input_iterator_constructed_sso(input_iter_data_sso.begin(), input_iter_data_sso.end(), alloc); + assert(verify_string(input_iterator_constructed_sso)); + +#if _HAS_CXX17 + str view_constructed(get_large_input_view(), alloc); + assert(verify_string(view_constructed)); + + str view_constructed_sso(get_sso_input_view(), alloc); + assert(verify_string(view_constructed_sso)); + + const string_view_convertible convertible; + const string_view_convertible_sso convertible_sso; + str conversion_constructed(convertible, alloc); + assert(verify_string(conversion_constructed)); + + str conversion_constructed_sso(convertible_sso, alloc); + assert(verify_string(conversion_constructed_sso)); + + str conversion_start_length_constructed(convertible, 2, 18, alloc); + assert(verify_string(conversion_start_length_constructed)); + + str conversion_start_length_constructed_sso(convertible, 18, 2, alloc); + assert(verify_string(conversion_start_length_constructed)); + + str conversion_start_length_constructed_sso_to_sso(convertible_sso, 1, 2, alloc); + assert(verify_string(conversion_start_length_constructed_sso_to_sso)); +#endif // _HAS_CXX17 + } +} + +template +void test_append() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 1; + constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + const str input_sso_growing(max_sso_size, CharType{'b'}); + + { // push_back + str push_back{input}; + push_back.push_back(CharType{'y'}); + assert(verify_string(push_back)); + + str push_back_sso{input_sso}; + push_back_sso.push_back(CharType{'y'}); + assert(verify_string(push_back_sso)); + + str push_back_growing{input_sso_growing}; + push_back_growing.push_back(CharType{'y'}); + assert(verify_string(push_back_growing)); + } + + { // append + str append_size_char{input}; + append_size_char.append(5, CharType{'a'}); + assert(verify_string(append_size_char)); + + str append_size_char_sso{input_sso}; + append_size_char_sso.append(2, CharType{'a'}); + assert(verify_string(append_size_char_sso)); + + str append_size_char_growing{input_sso_growing}; + append_size_char_growing.append(2, CharType{'a'}); + assert(verify_string(append_size_char_growing)); + + str append_str{input}; + append_str.append(input_sso); + assert(verify_string(append_str)); + + str append_str_sso{input_sso}; + append_str_sso.append(input_sso); + assert(verify_string(append_str_sso)); + + str append_str_growing{input_sso_growing}; + append_str_growing.append(input_sso); + assert(verify_string(append_str_growing)); + + str append_str_pos{input}; + append_str_pos.append(input_sso, 1); + assert(verify_string(append_str_pos)); + + str append_str_pos_sso{input_sso}; + append_str_pos_sso.append(input_sso, 1); + assert(verify_string(append_str_pos_sso)); + + str append_str_pos_growing{input_sso_growing}; + append_str_pos_growing.append(input_sso, 1); + assert(verify_string(append_str_pos_growing)); + + str append_str_pos_len{input}; + append_str_pos_len.append(input_sso, 1, 2); + assert(verify_string(append_str_pos_len)); + + str append_str_pos_len_sso{input_sso}; + append_str_pos_len_sso.append(input_sso, 1, 2); + assert(verify_string(append_str_pos_len_sso)); + + str append_str_pos_len_growing{input_sso_growing}; + append_str_pos_len_growing.append(input_sso, 1, 2); + assert(verify_string(append_str_pos_len_growing)); + + str append_literal{input}; + append_literal.append(get_sso_input()); + assert(verify_string(append_literal)); + + str append_literal_sso{input_sso}; + append_literal_sso.append(get_sso_input()); + assert(verify_string(append_literal_sso)); + + str append_literal_growing{input_sso_growing}; + append_literal_growing.append(get_sso_input()); + assert(verify_string(append_literal_growing)); + + str append_literal_size{input}; + append_literal_size.append(get_sso_input(), 2); + assert(verify_string(append_literal_size)); + + str append_literal_size_sso{input_sso}; + append_literal_size_sso.append(get_sso_input(), 2); + assert(verify_string(append_literal_size_sso)); + + str append_literal_size_growing{input_sso_growing}; + append_literal_size_growing.append(get_sso_input(), 2); + assert(verify_string(append_literal_size_growing)); + + str append_iterator{input}; + append_iterator.append(input_sso.begin(), input_sso.end()); + assert(verify_string(append_iterator)); + + str append_iterator_sso{input_sso}; + append_iterator_sso.append(input_sso.begin(), input_sso.end()); + assert(verify_string(append_iterator_sso)); + + str append_iterator_growing{input_sso_growing}; + append_iterator_growing.append(input_sso.begin(), input_sso.end()); + assert(verify_string(append_iterator_growing)); + + input_iterator_tester input_iter_data; + str append_input_iterator{input}; + append_input_iterator.append(input_iter_data.begin(), input_iter_data.end()); + assert(verify_string(append_input_iterator)); + + str append_input_iterator_sso{input_sso}; + append_input_iterator_sso.append(input_iter_data.begin(), input_iter_data.end()); + assert(verify_string(append_input_iterator_sso)); + + str append_input_iterator_growing{input_sso_growing}; + append_input_iterator_growing.append(input_iter_data.begin(), input_iter_data.end()); + assert(verify_string(append_input_iterator_growing)); + + str append_initializer_list{input}; + append_initializer_list.append({CharType{'b'}, CharType{'b'}}); + assert(verify_string(append_initializer_list)); + + str append_initializer_list_sso{input_sso}; + append_initializer_list_sso.append({CharType{'b'}, CharType{'b'}}); + assert(verify_string(append_initializer_list_sso)); + + str append_initializer_list_growing{input_sso_growing}; + append_initializer_list_growing.append({CharType{'b'}, CharType{'b'}}); + assert(verify_string(append_initializer_list_growing)); + +#if _HAS_CXX17 + const string_view_convertible_sso convertible; + str append_conversion{input}; + append_conversion.append(convertible); + assert(verify_string(append_conversion)); + + str append_conversion_sso{input_sso}; + append_conversion_sso.append(convertible); + assert(verify_string(append_conversion_sso)); + + str append_conversion_growing{input_sso_growing}; + append_conversion_growing.append(convertible); + assert(verify_string(append_conversion_growing)); + + str append_conversion_start_length{input}; + append_conversion_start_length.append(convertible, 1, 2); + assert(verify_string(append_conversion_start_length)); + + str append_conversion_start_length_sso{input_sso}; + append_conversion_start_length_sso.append(convertible, 1, 2); + assert(verify_string(append_conversion_start_length_sso)); + + str append_conversion_start_length_growing{input_sso_growing}; + append_conversion_start_length_growing.append(convertible, 1, 2); + assert(verify_string(append_conversion_start_length_growing)); +#endif // _HAS_CXX17 + } + + { // operator+= + str plus_string{input}; + plus_string += input_sso; + assert(verify_string(plus_string)); + + str plus_string_sso{input_sso}; + plus_string_sso += input_sso; + assert(verify_string(plus_string_sso)); + + str plus_string_growing{input_sso_growing}; + plus_string_growing += input_sso; + assert(verify_string(plus_string_growing)); + + str plus_character{input}; + plus_character += CharType{'a'}; + assert(verify_string(plus_character)); + + str plus_character_sso{input_sso}; + plus_character_sso += CharType{'a'}; + assert(verify_string(plus_character_sso)); + + str plus_character_growing{input_sso_growing}; + plus_character_growing += CharType{'a'}; + assert(verify_string(plus_character_growing)); + + str plus_literal{input}; + plus_literal += get_sso_input(); + assert(verify_string(plus_literal)); + + str plus_literal_sso{input_sso}; + plus_literal_sso += get_sso_input(); + assert(verify_string(plus_literal_sso)); + + str plus_literal_growing{input_sso_growing}; + plus_literal_growing += get_sso_input(); + assert(verify_string(plus_literal_growing)); + + str plus_initializer_list{input}; + plus_initializer_list += {CharType{'c'}, CharType{'a'}, CharType{'t'}}; + assert(verify_string(plus_initializer_list)); + + str plus_initializer_list_sso{input_sso}; + plus_initializer_list_sso += {CharType{'c'}, CharType{'a'}, CharType{'t'}}; + assert(verify_string(plus_initializer_list_sso)); + + str plus_initializer_list_growing{input_sso_growing}; + plus_initializer_list_growing += {CharType{'c'}, CharType{'a'}, CharType{'t'}}; + assert(verify_string(plus_initializer_list_growing)); + +#if _HAS_CXX17 + const string_view_convertible_sso convertible; + str plus_conversion{input}; + plus_conversion += convertible; + assert(verify_string(plus_conversion)); + + str plus_conversion_sso{input_sso}; + plus_conversion_sso += convertible; + assert(verify_string(plus_conversion_sso)); + + str plus_conversion_growing{input_sso_growing}; + plus_conversion_growing += convertible; + assert(verify_string(plus_conversion_growing)); +#endif // _HAS_CXX17 + } + + { // operator+ + str op_str_str_large_large = input + input; + assert(verify_string(op_str_str_large_large)); + + str op_str_str_large_sso = input + input_sso; + assert(verify_string(op_str_str_large_sso)); + + str op_str_str_sso_large = input_sso + input; + assert(verify_string(op_str_str_sso_large)); + + str op_str_str_sso_sso = input_sso + input_sso; + assert(verify_string(op_str_str_sso_sso)); + + str op_str_literal_large_large = input + get_large_input(); + assert(verify_string(op_str_literal_large_large)); + + str op_str_literal_large_sso = input + get_sso_input(); + assert(verify_string(op_str_literal_large_sso)); + + str op_str_literal_sso_large = input_sso + get_large_input(); + assert(verify_string(op_str_literal_sso_large)); + + str op_str_literal_sso_sso = input_sso + get_sso_input(); + assert(verify_string(op_str_literal_sso_sso)); + + str op_literal_str_large_large = get_large_input() + input; + assert(verify_string(op_literal_str_large_large)); + + str op_literal_str_large_sso = get_large_input() + input; + assert(verify_string(op_literal_str_large_sso)); + + str op_literal_str_sso_large = get_sso_input() + input_sso; + assert(verify_string(op_literal_str_sso_large)); + + str op_literal_str_sso_sso = get_sso_input() + input_sso; + assert(verify_string(op_literal_str_sso_sso)); + + str op_str_char_large = input + CharType{'!'}; + assert(verify_string(op_str_char_large)); + + str op_str_char_sso = input_sso + CharType{'!'}; + assert(verify_string(op_str_char_sso)); + + str op_str_char_sso_growing = input_sso_growing + CharType{'!'}; + assert(verify_string(op_str_char_sso_growing)); + + str op_char_str_large = CharType{'!'} + input; + assert(verify_string(op_char_str_large)); + + str op_char_str_sso = CharType{'!'} + input_sso; + assert(verify_string(op_char_str_sso)); + + str op_char_str_sso_growing = CharType{'!'} + input_sso_growing; + assert(verify_string(op_char_str_sso_growing)); + + // With rvalue input + str op_rstr_rstr_large_large = str(large_size, CharType{'b'}) + str(large_size, CharType{'b'}); + assert(verify_string(op_rstr_rstr_large_large)); + + str op_rstr_rstr_large_sso = str(large_size, CharType{'b'}) + str(sso_size, CharType{'b'}); + assert(verify_string(op_rstr_rstr_large_sso)); + + str op_rstr_rstr_sso_large = str(sso_size, CharType{'b'}) + str(large_size, CharType{'b'}); + assert(verify_string(op_rstr_rstr_sso_large)); + + str op_rstr_rstr_sso_sso = str(sso_size, CharType{'b'}) + str(sso_size, CharType{'b'}); + assert(verify_string(op_rstr_rstr_sso_sso)); + + str op_rstr_literal_large_large = str(large_size, CharType{'b'}) + get_large_input(); + assert(verify_string(op_rstr_literal_large_large)); + + str op_rstr_literal_large_sso = str(large_size, CharType{'b'}) + get_sso_input(); + assert(verify_string(op_rstr_literal_large_sso)); + + str op_rstr_literal_sso_large = str(sso_size, CharType{'b'}) + get_large_input(); + assert(verify_string(op_rstr_literal_sso_large)); + + str op_rstr_literal_sso_sso = str(sso_size, CharType{'b'}) + get_sso_input(); + assert(verify_string(op_rstr_literal_sso_sso)); + + str op_literal_rstr_large_large = get_large_input() + str(large_size, CharType{'b'}); + assert(verify_string(op_literal_rstr_large_large)); + + str op_literal_rstr_large_sso = get_large_input() + str(large_size, CharType{'b'}); + assert(verify_string(op_literal_rstr_large_sso)); + + str op_literal_rstr_sso_large = get_sso_input() + str(sso_size, CharType{'b'}); + assert(verify_string(op_literal_rstr_sso_large)); + + str op_literal_rstr_sso_sso = get_sso_input() + str(sso_size, CharType{'b'}); + assert(verify_string(op_literal_rstr_sso_sso)); + + str op_rstr_char_large = str(large_size, CharType{'b'}) + CharType{'!'}; + assert(verify_string(op_rstr_char_large)); + + str op_rstr_char_sso = str(sso_size, CharType{'b'}) + CharType{'!'}; + assert(verify_string(op_rstr_char_sso)); + + str op_rstr_char_sso_growing = str(max_sso_size, CharType{'b'}) + CharType{'!'}; + assert(verify_string(op_rstr_char_sso_growing)); + + str op_char_rstr_large = CharType{'!'} + str(large_size, CharType{'b'}); + assert(verify_string(op_char_rstr_large)); + + str op_char_rstr_sso = CharType{'!'} + str(sso_size, CharType{'b'}); + assert(verify_string(op_char_rstr_sso)); + + str op_char_rstr_sso_growing = CharType{'!'} + str(max_sso_size, CharType{'b'}); + assert(verify_string(op_char_rstr_sso_growing)); + } +} + +template +void test_assign() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 2; + + const str start(large_size - 1, CharType{'a'}); + const str start_sso(sso_size + 1, CharType{'a'}); + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + + input_iterator_tester input_iter_data; + input_iterator_tester input_iter_data_sso; + + { // assignment operator + str copy_assigned_large_large{start}; + copy_assigned_large_large = input; + assert(verify_string(copy_assigned_large_large)); + + str copy_assigned_large_sso{start}; + copy_assigned_large_sso = input_sso; + assert(verify_string(copy_assigned_large_sso)); + + str copy_assigned_sso_large{start_sso}; + copy_assigned_sso_large = input; + assert(verify_string(copy_assigned_sso_large)); + + str copy_assigned_sso_sso{start_sso}; + copy_assigned_sso_sso = input_sso; + assert(verify_string(copy_assigned_sso_sso)); + + str move_assigned_large_large{start}; + move_assigned_large_large = move(copy_assigned_large_large); + assert(verify_string(copy_assigned_large_large)); + assert(verify_string(move_assigned_large_large)); + + str move_assigned_large_sso{start}; + move_assigned_large_sso = move(copy_assigned_large_sso); + assert(verify_string(copy_assigned_large_sso)); + assert(verify_string(move_assigned_large_sso)); + + str move_assigned_sso_large{start_sso}; + move_assigned_sso_large = move(move_assigned_large_large); + assert(verify_string(move_assigned_large_large)); + assert(verify_string(move_assigned_sso_large)); + + str move_assigned_sso_sso{start_sso}; + move_assigned_sso_sso = move(move_assigned_large_sso); + assert(verify_string(move_assigned_large_sso)); + assert(verify_string(move_assigned_sso_sso)); + + str literal_assigned_large_large{start}; + literal_assigned_large_large = get_large_input(); + assert(verify_string(literal_assigned_large_large)); + + str literal_assigned_large_sso{start}; + literal_assigned_large_sso = get_sso_input(); + assert(verify_string(literal_assigned_large_sso)); + + str literal_assigned_sso_large{start_sso}; + literal_assigned_sso_large = get_large_input(); + assert(verify_string(literal_assigned_sso_large)); + + str literal_assigned_sso_sso{start_sso}; + literal_assigned_sso_sso = get_sso_input(); + assert(verify_string(literal_assigned_sso_sso)); + + str char_assigned_large{start}; + char_assigned_large = CharType{'!'}; + assert(verify_string(char_assigned_large)); + + str char_assigned_sso{start_sso}; + char_assigned_sso = CharType{'!'}; + assert(verify_string(char_assigned_sso)); + + str initializer_list_assigned_large_large{start}; + initializer_list_assigned_large_large = {CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, + CharType{'o'}, CharType{' '}, // + CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, + CharType{'k'}, CharType{'i'}, CharType{'t'}, CharType{'t'}, CharType{'e'}, CharType{'n'}, CharType{'s'}}; + assert(verify_string(initializer_list_assigned_large_large)); + + str initializer_list_assigned_large_sso{start}; + initializer_list_assigned_large_sso = {CharType{'c'}, CharType{'a'}, CharType{'t'}}; + assert(verify_string(initializer_list_assigned_large_sso)); + + str initializer_list_assigned_sso_large{start_sso}; + initializer_list_assigned_sso_large = {CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, + CharType{'o'}, CharType{' '}, // + CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, + CharType{'k'}, CharType{'i'}, CharType{'t'}, CharType{'t'}, CharType{'e'}, CharType{'n'}, CharType{'s'}}; + assert(verify_string(initializer_list_assigned_sso_large)); + + str initializer_list_assigned_sso_sso{start_sso}; + initializer_list_assigned_sso_sso = {CharType{'c'}, CharType{'a'}, CharType{'t'}}; + assert(verify_string(initializer_list_assigned_sso_sso)); + +#if _HAS_CXX17 + const string_view_convertible convertible; + const string_view_convertible_sso convertible_sso; + str conversion_assigned_large_large{start}; + conversion_assigned_large_large = convertible; + assert(verify_string(conversion_assigned_large_large)); + + str conversion_assigned_large_sso{start}; + conversion_assigned_large_sso = convertible_sso; + assert(verify_string(conversion_assigned_large_sso)); + + str conversion_assigned_sso_large{start_sso}; + conversion_assigned_sso_large = convertible; + assert(verify_string(conversion_assigned_sso_large)); + + str conversion_assigned_sso_sso{start_sso}; + conversion_assigned_sso_sso = convertible_sso; + assert(verify_string(conversion_assigned_sso_sso)); +#endif // _HAS_CXX17 + } + + { // assign + str assign_str_large_large{start}; + assign_str_large_large.assign(input); + assert(verify_string(assign_str_large_large)); + + str assign_str_large_sso{start}; + assign_str_large_sso.assign(input_sso); + assert(verify_string(assign_str_large_sso)); + + str assign_str_sso_large{start_sso}; + assign_str_sso_large.assign(input); + assert(verify_string(assign_str_sso_large)); + + str assign_str_sso_sso{start_sso}; + assign_str_sso_sso.assign(input_sso); + assert(verify_string(assign_str_sso_sso)); + + str assign_str_size_large_large{start}; + assign_str_size_large_large.assign(input, 1); + assert(verify_string(assign_str_size_large_large)); + + str assign_str_size_large_sso{start}; + assign_str_size_large_sso.assign(input_sso, 1); + assert(verify_string(assign_str_size_large_sso)); + + str assign_str_size_sso_large{start_sso}; + assign_str_size_sso_large.assign(input, 1); + assert(verify_string(assign_str_size_sso_large)); + + str assign_str_size_sso_sso{start_sso}; + assign_str_size_sso_sso.assign(input_sso, 1); + assert(verify_string(assign_str_size_sso_sso)); + + str assign_str_len_size_large_large{start}; + assign_str_len_size_large_large.assign(input, 1, large_size - 1); + assert(verify_string(assign_str_len_size_large_large)); + + str assign_str_size_len_large_sso{start}; + assign_str_size_len_large_sso.assign(input_sso, 1, sso_size - 1); + assert(verify_string(assign_str_size_len_large_sso)); + + str assign_str_size_len_sso_large{start_sso}; + assign_str_size_len_sso_large.assign(input, 1, large_size - 1); + assert(verify_string(assign_str_size_len_sso_large)); + + str assign_str_size_len_sso_sso{start_sso}; + assign_str_size_len_sso_sso.assign(input_sso, 1, sso_size - 1); + assert(verify_string(assign_str_size_len_sso_sso)); + + str assign_rstr_large_large{start}; + assign_rstr_large_large.assign(move(assign_str_large_large)); + assert(verify_string(assign_str_large_large)); + assert(verify_string(assign_rstr_large_large)); + + str assign_rstr_large_sso{start}; + assign_rstr_large_sso.assign(move(assign_str_large_sso)); + assert(verify_string(assign_str_large_sso)); + assert(verify_string(assign_rstr_large_sso)); + + str assign_rstr_sso_large{start_sso}; + assign_rstr_sso_large.assign(move(assign_str_sso_large)); + assert(verify_string(assign_str_sso_large)); + assert(verify_string(assign_rstr_sso_large)); + + str assign_rstr_sso_sso{start_sso}; + assign_rstr_sso_sso.assign(move(assign_str_sso_sso)); + assert(verify_string(assign_str_sso_sso)); + assert(verify_string(assign_rstr_sso_sso)); + + str assign_literal_large_large{start}; + assign_literal_large_large.assign(get_large_input()); + assert(verify_string(assign_literal_large_large)); + + str assign_literal_large_sso{start}; + assign_literal_large_sso.assign(get_sso_input()); + assert(verify_string(assign_literal_large_sso)); + + str assign_literal_sso_large{start_sso}; + assign_literal_sso_large.assign(get_large_input()); + assert(verify_string(assign_literal_sso_large)); + + str assign_literal_sso_sso{start_sso}; + assign_literal_sso_sso.assign(get_sso_input()); + assert(verify_string(assign_literal_sso_sso)); + + str assign_literal_count_large_large{start}; + assign_literal_count_large_large.assign(get_large_input(), 18); + assert(verify_string(assign_literal_count_large_large)); + + str assign_literal_count_large_sso{start}; + assign_literal_count_large_sso.assign(get_sso_input(), 3); + assert(verify_string(assign_literal_count_large_sso)); + + str assign_literal_count_sso_large{start_sso}; + assign_literal_count_sso_large.assign(get_large_input(), 18); + assert(verify_string(assign_literal_count_sso_large)); + + str assign_literal_count_sso_sso{start_sso}; + assign_literal_count_sso_sso.assign(get_sso_input(), 3); + assert(verify_string(assign_literal_count_sso_sso)); + + str assign_char_count_large_large{start}; + assign_char_count_large_large.assign(18, CharType{'c'}); + assert(verify_string(assign_char_count_large_large)); + + str assign_char_count_large_sso{start}; + assign_char_count_large_sso.assign(3, CharType{'c'}); + assert(verify_string(assign_char_count_large_sso)); + + str assign_char_count_sso_large{start_sso}; + assign_char_count_sso_large.assign(18, CharType{'c'}); + assert(verify_string(assign_char_count_sso_large)); + + str assign_char_count_sso_sso{start_sso}; + assign_char_count_sso_sso.assign(3, CharType{'c'}); + assert(verify_string(assign_char_count_sso_sso)); + + str assign_iterator_large_large{start}; + assign_iterator_large_large.assign(input.begin(), input.end()); + assert(verify_string(assign_iterator_large_large)); + + str assign_iterator_large_sso{start}; + assign_iterator_large_sso.assign(input_sso.begin(), input_sso.end()); + assert(verify_string(assign_iterator_large_sso)); + + str assign_iterator_sso_large{start_sso}; + assign_iterator_sso_large.assign(input.begin(), input.end()); + assert(verify_string(assign_iterator_sso_large)); + + str assign_iterator_sso_sso{start_sso}; + assign_iterator_sso_sso.assign(input_sso.begin(), input_sso.end()); + assert(verify_string(assign_iterator_sso_sso)); + + str assign_input_iterator_large_large{start}; + assign_input_iterator_large_large.assign(input_iter_data.begin(), input_iter_data.end()); + assert(verify_string(assign_input_iterator_large_large)); + + str assign_input_iterator_large_sso{start}; + assign_input_iterator_large_sso.assign(input_iter_data_sso.begin(), input_iter_data_sso.end()); + assert(verify_string(assign_input_iterator_large_sso)); + + str assign_input_iterator_sso_large{start_sso}; + assign_input_iterator_sso_large.assign(input_iter_data.begin(), input_iter_data.end()); + assert(verify_string(assign_input_iterator_sso_large)); + + str assign_input_iterator_sso_sso{start_sso}; + assign_input_iterator_sso_sso.assign(input_iter_data_sso.begin(), input_iter_data_sso.end()); + assert(verify_string(assign_input_iterator_sso_sso)); + + str assign_initializer_list_large_large{start}; + assign_initializer_list_large_large.assign({CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, + CharType{'o'}, CharType{' '}, // + CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, + CharType{'k'}, CharType{'i'}, CharType{'t'}, CharType{'t'}, CharType{'e'}, CharType{'n'}, CharType{'s'}}); + assert(verify_string(assign_initializer_list_large_large)); + + str assign_initializer_list_large_sso{start}; + assign_initializer_list_large_sso.assign({CharType{'c'}, CharType{'a'}, CharType{'t'}}); + assert(verify_string(assign_initializer_list_large_large)); + + str assign_initializer_list_sso_large{start_sso}; + assign_initializer_list_sso_large.assign({CharType{'H'}, CharType{'e'}, CharType{'l'}, CharType{'l'}, + CharType{'o'}, CharType{' '}, // + CharType{'f'}, CharType{'l'}, CharType{'u'}, CharType{'f'}, CharType{'f'}, CharType{'y'}, CharType{' '}, + CharType{'k'}, CharType{'i'}, CharType{'t'}, CharType{'t'}, CharType{'e'}, CharType{'n'}, CharType{'s'}}); + assert(verify_string(assign_initializer_list_sso_large)); + + str assign_initializer_list_sso_sso{start_sso}; + assign_initializer_list_sso_sso.assign({CharType{'c'}, CharType{'a'}, CharType{'t'}}); + assert(verify_string(assign_initializer_list_sso_sso)); + +#if _HAS_CXX17 + const string_view_convertible convertible; + const string_view_convertible_sso convertible_sso; + + str assign_conversion_large_large{start}; + assign_conversion_large_large.assign(convertible); + assert(verify_string(assign_conversion_large_large)); + + str assign_conversion_large_sso{start}; + assign_conversion_large_sso.assign(convertible_sso); + assert(verify_string(assign_conversion_large_sso)); + + str assign_conversion_sso_large{start_sso}; + assign_conversion_sso_large.assign(convertible); + assert(verify_string(assign_conversion_sso_large)); + + str assign_conversion_sso_sso{start_sso}; + assign_conversion_sso_sso.assign(convertible_sso); + assert(verify_string(assign_conversion_sso_sso)); + + str assign_conversion_start_length_large_large{start}; + assign_conversion_start_length_large_large.assign(convertible, 2, 18); + assert(verify_string(assign_conversion_start_length_large_large)); + + str assign_conversion_start_length_large_sso{start}; + assign_conversion_start_length_large_sso.assign(convertible_sso, 1, 2); + assert(verify_string(assign_conversion_start_length_large_sso)); + + str assign_conversion_start_length_sso_large{start_sso}; + assign_conversion_start_length_sso_large.assign(convertible, 2, 18); + assert(verify_string(assign_conversion_start_length_sso_large)); + + str assign_conversion_start_length_sso_sso{start_sso}; + assign_conversion_start_length_sso_sso.assign(convertible_sso, 1, 2); + assert(verify_string(assign_conversion_start_length_sso_sso)); +#endif // _HAS_CXX17 + } +} + +template +void test_insertion() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 1; + constexpr size_t max_sso_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)) - 1; + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + const str input_sso_growing(max_sso_size, CharType{'b'}); + + input_iterator_tester input_iter_data_sso; + + { // insert + const CharType to_be_inserted = CharType{','}; + str insert_char{input}; + insert_char.insert(insert_char.begin(), to_be_inserted); + assert(verify_string(insert_char)); + + str insert_char_sso{input_sso}; + insert_char_sso.insert(insert_char_sso.begin(), to_be_inserted); + assert(verify_string(insert_char_sso)); + + str insert_char_growing{input_sso_growing}; + insert_char_growing.insert(insert_char_growing.begin(), to_be_inserted); + assert(verify_string(insert_char_growing)); + + str insert_char_rvalue{input}; + insert_char_rvalue.insert(insert_char_rvalue.begin(), CharType{'a'}); + assert(verify_string(insert_char_rvalue)); + + str insert_char_rvalue_sso{input_sso}; + insert_char_rvalue_sso.insert(insert_char_rvalue_sso.begin(), CharType{'a'}); + assert(verify_string(insert_char_rvalue_sso)); + + str insert_char_rvalue_growing{input_sso_growing}; + insert_char_rvalue_growing.insert(insert_char_rvalue_growing.begin(), CharType{'a'}); + assert(verify_string(insert_char_rvalue_growing)); + + str insert_iter_count_char{input}; + insert_iter_count_char.insert(insert_iter_count_char.begin(), 2, CharType{'a'}); + assert(verify_string(insert_iter_count_char)); + + str insert_iter_count_char_sso{input_sso}; + insert_iter_count_char_sso.insert(insert_iter_count_char_sso.begin(), 2, CharType{'a'}); + assert(verify_string(insert_iter_count_char_sso)); + + str insert_iter_count_char_growing{input_sso_growing}; + insert_iter_count_char_growing.insert(insert_iter_count_char_growing.begin(), 2, CharType{'a'}); + assert(verify_string(insert_iter_count_char_growing)); + + str insert_iter{input}; + insert_iter.insert(insert_iter.begin(), input_sso.begin(), input_sso.end()); + assert(verify_string(insert_iter)); + + str insert_iter_sso{input_sso}; + insert_iter_sso.insert(insert_iter_sso.begin(), input_sso.begin(), input_sso.end()); + assert(verify_string(insert_iter_sso)); + + str insert_iter_growing{input_sso_growing}; + insert_iter_growing.insert(insert_iter_growing.begin(), input_sso.begin(), input_sso.end()); + assert(verify_string(insert_iter_growing)); + + str insert_input_iter{input}; + insert_input_iter.insert(insert_input_iter.begin(), input_iter_data_sso.begin(), input_iter_data_sso.end()); + assert(verify_string(insert_input_iter)); + + str insert_input_iter_sso{input_sso}; + insert_input_iter_sso.insert( + insert_input_iter_sso.begin(), input_iter_data_sso.begin(), input_iter_data_sso.end()); + assert(verify_string(insert_input_iter_sso)); + + str insert_input_iter_growing{input_sso_growing}; + insert_input_iter_growing.insert( + insert_input_iter_growing.begin(), input_iter_data_sso.begin(), input_iter_data_sso.end()); + assert(verify_string(insert_input_iter_growing)); + + str insert_initializer_list{input}; + insert_initializer_list.insert(insert_initializer_list.begin(), {CharType{'c'}, CharType{'a'}, CharType{'t'}}); + assert(verify_string(insert_initializer_list)); + + str insert_initializer_list_sso{input_sso}; + insert_initializer_list_sso.insert( + insert_initializer_list_sso.begin(), {CharType{'c'}, CharType{'a'}, CharType{'t'}}); + assert(verify_string(insert_initializer_list_sso)); + + str insert_initializer_list_growing{input_sso_growing}; + insert_initializer_list_growing.insert( + insert_initializer_list_growing.begin(), {CharType{'c'}, CharType{'a'}, CharType{'t'}}); + assert(verify_string(insert_initializer_list_growing)); + + str insert_pos_str{input}; + insert_pos_str.insert(0, input_sso); + assert(verify_string(insert_pos_str)); + + str insert_pos_str_sso{input_sso}; + insert_pos_str_sso.insert(0, input_sso); + assert(verify_string(insert_pos_str_sso)); + + str insert_pos_str_growing{input_sso_growing}; + insert_pos_str_growing.insert(0, input_sso); + assert(verify_string(insert_pos_str_growing)); + + str insert_pos_substr{input}; + insert_pos_substr.insert(0, input_sso, 1, 2); + assert(verify_string(insert_pos_substr)); + + str insert_pos_substr_sso{input_sso}; + insert_pos_substr_sso.insert(0, input_sso, 1, 2); + assert(verify_string(insert_pos_substr_sso)); + + str insert_pos_substr_growing{input_sso_growing}; + insert_pos_substr_growing.insert(0, input_sso, 1, 2); + assert(verify_string(insert_pos_substr_growing)); + +#if _HAS_CXX17 + const string_view_convertible_sso convertible; + str insert_pos_conversion{input}; + insert_pos_conversion.insert(0, convertible); + assert(verify_string(insert_pos_conversion)); + + str insert_pos_conversion_sso{input_sso}; + insert_pos_conversion_sso.insert(0, convertible); + assert(verify_string(insert_pos_conversion_sso)); + + str insert_pos_conversion_growing{input_sso_growing}; + insert_pos_conversion_growing.insert(0, convertible); + assert(verify_string(insert_pos_conversion_growing)); + + str insert_pos_conversion_substr{input}; + insert_pos_conversion.insert(0, convertible, 1, 2); + assert(verify_string(insert_pos_conversion)); + + str insert_pos_conversion_substr_sso{input_sso}; + insert_pos_conversion_sso.insert(0, convertible, 1, 2); + assert(verify_string(insert_pos_conversion_sso)); + + str insert_pos_conversion_substr_growing{input_sso_growing}; + insert_pos_conversion_growing.insert(0, convertible, 1, 2); + assert(verify_string(insert_pos_conversion_growing)); +#endif // _HAS_CXX17 + + str insert_pos_literal{input}; + insert_pos_literal.insert(0, get_sso_input()); + assert(verify_string(insert_pos_literal)); + + str insert_pos_literal_sso{input_sso}; + insert_pos_literal_sso.insert(0, get_sso_input()); + assert(verify_string(insert_pos_literal_sso)); + + str insert_pos_literal_growing{input_sso_growing}; + insert_pos_literal_growing.insert(0, get_sso_input()); + assert(verify_string(insert_pos_literal_growing)); + + str insert_pos_literal_substr{input}; + insert_pos_literal_substr.insert(0, get_sso_input(), 1); + assert(verify_string(insert_pos_literal_substr)); + + str insert_pos_literal_substr_sso{input_sso}; + insert_pos_literal_substr_sso.insert(0, get_sso_input(), 1); + assert(verify_string(insert_pos_literal_substr_sso)); + + str insert_pos_literal_substr_growing{input_sso_growing}; + insert_pos_literal_substr_growing.insert(0, get_sso_input(), 1); + assert(verify_string(insert_pos_literal_substr_growing)); + + str insert_pos_count_char{input}; + insert_pos_count_char.insert(0, 2, CharType{'a'}); + assert(verify_string(insert_pos_count_char)); + + str insert_pos_count_char_sso{input_sso}; + insert_pos_count_char_sso.insert(0, 2, CharType{'a'}); + assert(verify_string(insert_pos_count_char_sso)); + + str insert_pos_count_char_growing{input_sso_growing}; + insert_pos_count_char_growing.insert(0, 2, CharType{'a'}); + assert(verify_string(insert_pos_count_char_growing)); + } +} + +template +void test_removal() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 2; + constexpr size_t min_large_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)); + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + const str input_min_large(min_large_size, CharType{'b'}); + + { // clear + str cleared{input}; + cleared.clear(); + assert(verify_string(cleared)); + + str cleared_sso{input_sso}; + cleared_sso.clear(); + assert(verify_string(cleared_sso)); + } + + { // erase + str erase_pos_count{input}; + erase_pos_count.erase(0, 2); + assert(verify_string(erase_pos_count)); + + str erase_pos_count_sso{input_sso}; + erase_pos_count_sso.erase(0, 2); + assert(verify_string(erase_pos_count_sso)); + + str erase_pos_count_shrinking{input_min_large}; + erase_pos_count_shrinking.erase(0, 2); + assert(verify_string(erase_pos_count_shrinking)); + + str erase_iter{input}; + erase_iter.erase(erase_iter.begin()); + assert(verify_string(erase_iter)); + + str erase_iter_sso{input_sso}; + erase_iter_sso.erase(erase_iter_sso.begin()); + assert(verify_string(erase_iter_sso)); + + str erase_iter_shrinking{input_min_large}; + erase_iter_shrinking.erase(erase_iter_shrinking.begin()); + assert(verify_string(erase_iter_shrinking)); + + str erase_const_iter{input}; + erase_const_iter.erase(erase_const_iter.cbegin()); + assert(verify_string(erase_const_iter)); + + str erase_const_iter_sso{input_sso}; + erase_const_iter_sso.erase(erase_const_iter_sso.cbegin()); + assert(verify_string(erase_const_iter_sso)); + + str erase_const_iter_shrinking{input_min_large}; + erase_const_iter_shrinking.erase(erase_const_iter_shrinking.cbegin()); + assert(verify_string(erase_const_iter_shrinking)); + + str erase_iter_iter{input}; + erase_iter_iter.erase(erase_iter_iter.begin(), erase_iter_iter.begin() + 1); + assert(verify_string(erase_iter_iter)); + + str erase_iter_iter_sso{input_sso}; + erase_iter_iter_sso.erase(erase_iter_iter_sso.begin(), erase_iter_iter_sso.begin() + 1); + assert(verify_string(erase_iter_iter_sso)); + + str erase_iter_iter_shrinking{input_min_large}; + erase_iter_iter_shrinking.erase(erase_iter_iter_shrinking.begin(), erase_iter_iter_shrinking.begin() + 1); + assert(verify_string(erase_iter_iter_shrinking)); + + str erase_const_iter_iter{input}; + erase_const_iter_iter.erase(erase_const_iter_iter.begin(), erase_const_iter_iter.begin() + 1); + assert(verify_string(erase_const_iter_iter)); + + str erase_const_iter_iter_sso{input_sso}; + erase_const_iter_iter_sso.erase(erase_const_iter_iter_sso.begin(), erase_const_iter_iter_sso.begin() + 1); + assert(verify_string(erase_const_iter_iter_sso)); + + str erase_const_iter_iter_shrinking{input_min_large}; + erase_const_iter_iter_shrinking.erase( + erase_const_iter_iter_shrinking.begin(), erase_const_iter_iter_shrinking.begin() + 1); + assert(verify_string(erase_const_iter_iter_shrinking)); + +#if _HAS_CXX20 + str erased_free{get_large_input()}; + erase(erased_free, CharType{'l'}); + assert(verify_string(erased_free)); + + str erased_free_sso{get_sso_input()}; + erase(erased_free_sso, CharType{'l'}); + assert(verify_string(erased_free_sso)); + + str erased_free_if{get_large_input()}; + erase_if(erased_free_if, [](const CharType val) { return val == CharType{'t'}; }); + assert(verify_string(erased_free_if)); + + str erased_free_if_sso{get_sso_input()}; + erase_if(erased_free_if_sso, [](const CharType val) { return val == CharType{'t'}; }); + assert(verify_string(erased_free_if_sso)); +#endif // _HAS_CXX20 + } + + { // pop_back + str pop_back{input}; + pop_back.pop_back(); + assert(verify_string(pop_back)); + + str pop_back_sso{input_sso}; + pop_back_sso.pop_back(); + assert(verify_string(pop_back_sso)); + + str pop_back_shrinking{input_min_large}; + pop_back_shrinking.pop_back(); + assert(verify_string(pop_back_shrinking)); + } + + { // shrink_to_fit + str shrink_to_fit(32, CharType{'a'}); + shrink_to_fit.resize(min_large_size); + shrink_to_fit.shrink_to_fit(); + assert(verify_string(shrink_to_fit)); + + str shrink_to_fit_sso{input_sso}; + shrink_to_fit_sso.pop_back(); + shrink_to_fit_sso.shrink_to_fit(); + assert(verify_string(shrink_to_fit_sso)); + + str shrink_to_fit_shrinking{input_min_large}; + shrink_to_fit_shrinking.pop_back(); + shrink_to_fit_shrinking.shrink_to_fit(); + assert(verify_string(shrink_to_fit_shrinking)); + } +} + +template +void test_misc() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 2; + constexpr size_t min_large_size = (16 / sizeof(CharType) < 1 ? 1 : 16 / sizeof(CharType)); + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + const str input_min_large(min_large_size, CharType{'b'}); + + { // substr + str substr_large_to_large = input.substr(2); + assert(verify_string(substr_large_to_large)); + + str substr_large_to_sso = input.substr(18); + assert(verify_string(substr_large_to_sso)); + + str substr_sso = input_sso.substr(1); + assert(verify_string(substr_sso)); + + str substr_count_large_to_large = input.substr(2, 18); + assert(verify_string(substr_count_large_to_large)); + + str substr_count_large_to_sso = input.substr(2, 2); + assert(verify_string(substr_large_to_sso)); + + str substr_count_sso = input_sso.substr(1, 2); + assert(verify_string(substr_sso)); + } + + { // resize + str resize_large_to_large{input}; + resize_large_to_large.resize(35); + assert(verify_string(resize_large_to_large)); + + str resize_large_to_sso{input}; + resize_large_to_sso.resize(3); + assert(verify_string(resize_large_to_sso)); + + str resize_sso_to_large{input_sso}; + resize_sso_to_large.resize(35); + assert(verify_string(resize_sso_to_large)); + + str resize_sso_to_sso{input_sso}; + resize_sso_to_sso.resize(3); + assert(verify_string(resize_sso_to_sso)); + + str resize_char_large_to_large{input}; + resize_char_large_to_large.resize(35, CharType{'c'}); + assert(verify_string(resize_char_large_to_large)); + + str resize_char_large_to_sso{input}; + resize_char_large_to_sso.resize(3, CharType{'c'}); + assert(verify_string(resize_char_large_to_sso)); + + str resize_char_sso_to_large{input_sso}; + resize_char_sso_to_large.resize(35, CharType{'c'}); + assert(verify_string(resize_char_sso_to_large)); + + str resize_char_sso_to_sso{input_sso}; + resize_char_sso_to_sso.resize(3, CharType{'c'}); + assert(verify_string(resize_char_sso_to_sso)); + } + + if constexpr (allocator_traits::propagate_on_container_swap::value) { // swap + str first_large{input}; + str second_large = input + str{CharType{'c'}, CharType{'a'}, CharType{'t'}}; + + str first_sso{input_sso}; + str second_sso = input_sso + CharType{'s'}; + + first_large.swap(second_large); + assert(verify_string(first_large)); + assert(verify_string(second_large)); + + first_sso.swap(second_sso); + assert(verify_string(first_sso)); + assert(verify_string(second_sso)); + + first_large.swap(first_sso); + assert(verify_string(first_large)); + assert(verify_string(first_sso)); + + second_sso.swap(second_large); + assert(verify_string(second_sso)); + assert(verify_string(second_large)); + } +} + +template +void test_sstream() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + using stream = basic_stringbuf, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 2; + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + + { // RValue construction + stream constructed_from_empty{str{}}; + str buffered_empty = move(constructed_from_empty).str(); + assert(verify_string(buffered_empty)); + assert(buffered_empty.empty()); + + str to_be_moved = input; + stream constructed_from_large{move(to_be_moved)}; + str buffered = move(constructed_from_large).str(); + assert(verify_string(to_be_moved)); + assert(verify_string(buffered)); + + str to_be_moved_sso = input_sso; + stream constructed_from_sso{move(to_be_moved_sso)}; + str buffered_sso = move(constructed_from_sso).str(); + assert(verify_string(to_be_moved_sso)); + assert(verify_string(buffered_sso)); + } + + { // RValue str + str to_be_moved = input; + stream str_large; + str_large.str(move(to_be_moved)); + str buffered = move(str_large).str(); + assert(verify_string(to_be_moved)); + assert(verify_string(buffered)); + + str to_be_moved_sso = input_sso; + stream str_sso; + str_sso.str(move(to_be_moved_sso)); + str buffered_sso = move(str_sso).str(); + assert(verify_string(to_be_moved_sso)); + assert(verify_string(buffered_sso)); + } +} + +template +void test_exceptions() { + using CharType = typename Alloc::value_type; + using str = basic_string, Alloc>; + + constexpr size_t large_size = 20; + constexpr size_t sso_size = 2; + + const str input(large_size, CharType{'b'}); + const str input_sso(sso_size, CharType{'b'}); + + throw_on_conversion iter_data[1] = {CharType{'b'}}; + input_iterator_tester, 1> input_iter_data; + + { // append + str append_iterator{input}; + try { + assert(verify_string(append_iterator)); + append_iterator.append(begin(iter_data), end(iter_data)); + assert(false); + } catch (...) { + assert(verify_string(append_iterator)); + } + +#if 0 // TRANSITION, DevCom-1527920 + str append_iterator_sso{input_sso}; + try { + assert(verify_string(append_iterator_sso)); + append_iterator_sso.append(begin(iter_data), end(iter_data)); + assert(false); + } catch (...) { + assert(verify_string(append_iterator_sso)); + } +#endif + + str append_input_iterator{input}; + try { + assert(verify_string(append_input_iterator)); + append_input_iterator.append(input_iter_data.begin(), input_iter_data.end()); + assert(false); + } catch (...) { + assert(verify_string(append_input_iterator)); + } + +#if 0 // TRANSITION, DevCom-1527920 + str append_input_iterator_sso{input_sso}; + try { + append_input_iterator_sso.append(input_iter_data.begin(), input_iter_data.end()); + } catch (...) { + assert(verify_string(append_input_iterator_sso)); + } +#endif + } + + { // assign + str assign_iterator{input}; + try { + assert(verify_string(assign_iterator)); + assign_iterator.assign(begin(iter_data), end(iter_data)); + assert(false); + } catch (...) { + assert(verify_string(assign_iterator)); + } + +#if 0 // TRANSITION, DevCom-1527920 + str assign_iterator_sso{input_sso}; + try { + assert(verify_string(assign_iterator_sso)); + assign_iterator_sso.assign(begin(iter_data), end(iter_data)); + } catch (...) { + assert(verify_string(assign_iterator_sso)); + } +#endif + + str assign_input_iterator{input}; + try { + assert(verify_string(assign_input_iterator)); + assign_input_iterator.assign(input_iter_data.begin(), input_iter_data.end()); + assert(false); + } catch (...) { + assert(verify_string(assign_input_iterator)); + } + +#if 0 // TRANSITION, DevCom-1527920 + str assign_input_iterator_sso{input_sso}; + try { + assert(verify_string(assign_input_iterator_sso)); + assign_input_iterator_sso.assign(input_iter_data.begin(), input_iter_data.end()); + assert(false); + } catch (...) { + assert(verify_string(assign_input_iterator_sso)); + } +#endif + } + + { // insert + str insert_iterator{input}; + try { + assert(verify_string(insert_iterator)); + insert_iterator.insert(insert_iterator.begin(), begin(iter_data), end(iter_data)); + assert(false); + } catch (...) { + assert(verify_string(insert_iterator)); + } + +#if 0 // TRANSITION, DevCom-1527920 + str insert_iterator_sso{input_sso}; + try { + assert(verify_string(insert_iterator_sso)); + insert_iterator_sso.insert(insert_iterator_sso.begin(), begin(iter_data), end(iter_data)); + assert(false); + } catch (...) { + assert(verify_string(insert_iterator_sso)); + } +#endif + + str insert_input_iterator{input}; + try { + assert(verify_string(insert_input_iterator)); + insert_input_iterator.insert(insert_input_iterator.begin(), input_iter_data.begin(), input_iter_data.end()); + assert(false); + } catch (...) { + assert(verify_string(insert_input_iterator)); + } + +#if 0 // TRANSITION, DevCom-1527920 + str insert_input_iterator_sso{input_sso}; + try { + assert(verify_string(insert_input_iterator_sso)); + insert_input_iterator_sso.insert( + insert_input_iterator_sso.begin(), input_iter_data.begin(), input_iter_data.end()); + assert(false); + } catch (...) { + assert(verify_string(insert_input_iterator_sso)); + } +#endif + } +} + +template +void run_tests() { + test_construction(); + test_append(); + test_assign(); + test_insertion(); + test_removal(); + test_misc(); + test_sstream(); +#if !(defined(__clang__) && defined(_M_IX86)) // TRANSITION, LLVM-54804 + test_exceptions(); +#endif // !(defined(__clang__) && defined(_M_IX86)) +} + +template class Alloc> +void run_custom_allocator_matrix() { + run_tests>(); + run_tests>(); + run_tests>(); + run_tests>(); +} + +template +void run_allocator_matrix() { + run_tests>(); + run_custom_allocator_matrix(); + run_custom_allocator_matrix(); + run_custom_allocator_matrix(); +} + +int main() { + run_allocator_matrix(); +#ifdef __cpp_char8_t + run_allocator_matrix(); +#endif // __cpp_char8_t + run_allocator_matrix(); + run_allocator_matrix(); + run_allocator_matrix(); +}