diff --git a/stl/inc/regex b/stl/inc/regex index 32ef9439b46..0cd17c2b3a0 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -936,7 +936,7 @@ basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, } // FORWARD DECLARATIONS -template >> +template >> class match_results; template diff --git a/stl/inc/sstream b/stl/inc/sstream index e5c6235cf2f..90ce15f8ea0 100644 --- a/stl/inc/sstream +++ b/stl/inc/sstream @@ -29,6 +29,10 @@ public: using _Mystr = basic_string<_Elem, _Traits, _Alloc>; using _Mysize_type = typename _Mystr::size_type; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_stringbuf() : _Seekhigh(nullptr), _Mystate(_Getstate(ios_base::in | ios_base::out)), _Al() {} explicit basic_stringbuf(ios_base::openmode _Mode) : _Seekhigh(nullptr), _Mystate(_Getstate(_Mode)), _Al() {} @@ -38,6 +42,37 @@ public: _Init(_Str.c_str(), _Str.size(), _Getstate(_Mode)); } +#if _HAS_CXX20 + explicit basic_stringbuf(const _Alloc& _Al_) : basic_stringbuf(ios_base::in | ios_base::out, _Al_) {} + + basic_stringbuf(ios_base::openmode _Mode, const _Alloc& _Al_) + : _Seekhigh(nullptr), _Mystate(_Getstate(_Mode)), _Al(_Al_) {} + + explicit basic_stringbuf(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : _Al(_STD move(_Str._Getal())) { + _Init_string_inplace(_STD move(_Str), _Getstate(_Mode)); + } + + template + basic_stringbuf(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al_) + : _Al(_Al_) { + _Init(_Str.c_str(), _Str.size(), _Getstate(_Mode)); + } + + template + basic_stringbuf(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al_) + : basic_stringbuf(_Str, ios_base::in | ios_base::out, _Al_) {} + + template , int> = 0> + explicit basic_stringbuf( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : basic_stringbuf(_Str, _Mode, _Alloc{}) {} + + basic_stringbuf(basic_stringbuf&& _Right, const _Alloc& _Al_) : _Al(_Al_) { + _Assign_rv(_STD move(_Right)); + } +#endif // _HAS_CXX20 + basic_stringbuf(basic_stringbuf&& _Right) { _Assign_rv(_STD move(_Right)); } @@ -54,12 +89,18 @@ public: } } - void swap(basic_stringbuf& _Right) { + void swap(basic_stringbuf& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { + if constexpr (!allocator_traits<_Alloc>::propagate_on_container_swap::value) { + _STL_ASSERT( + _Al == _Right._Al, "The allocators of basic_stringbuf should propagate or be equal on swap."); + } + _Mysb::swap(_Right); _STD swap(_Seekhigh, _Right._Seekhigh); _STD swap(_Mystate, _Right._Mystate); - _Swap_adl(_Al, _Right._Al); + // The same as basic_string::swap + _Pocs(_Al, _Right._Al); } } @@ -71,27 +112,53 @@ public: } enum { // constants for bits in stream state - _Allocated = 1, // set if character array storage has been allocated, eback() points to allocated storage - _Constant = 2, // set if character array nonmutable - _Noread = 4, // set if character array cannot be read - _Append = 8, // set if all writes are appends - _Atend = 16 // set if initial writes are appends + _Allocated = 1, // set if character array storage has been allocated, eback() points to allocated storage + _Constant = 2, // set if character array nonmutable + _Noread = 4, // set if character array cannot be read + _Append = 8, // set if all writes are appends + _Atend = 16, // set if initial writes are appends + _From_rvalue = 32 // set if character array is released from an rvalue of basic_string }; using int_type = typename _Traits::int_type; using pos_type = typename _Traits::pos_type; using off_type = typename _Traits::off_type; - _NODISCARD _Mystr str() const { - _Mystr _Result(_Al); - if (!(_Mystate & _Constant) && _Mysb::pptr()) { // writable, make string from write buffer + struct _Buffer_view { + _Elem* _Ptr; + _Mysize_type _Size; + _Mysize_type _Res; + }; + + _NODISCARD _Buffer_view _Get_buffer_view() const noexcept { + _Buffer_view _Result{}; + if ((!(_Mystate & _Constant) || (_Mystate & _From_rvalue)) && _Mysb::pptr()) { + // writable, make string view from write buffer const auto _Base = _Mysb::pbase(); - _Result.assign(_Base, static_cast<_Mysize_type>((_STD max)(_Mysb::pptr(), _Seekhigh) - _Base)); - } else if (!(_Mystate & _Noread) && _Mysb::gptr()) { // readable, make string from read buffer + _Result._Ptr = _Base; + _Result._Size = static_cast<_Mysize_type>((_STD max)(_Mysb::pptr(), _Seekhigh) - _Base); + _Result._Res = static_cast<_Mysize_type>(_Mysb::epptr() - _Base); + } else if (!(_Mystate & _Noread) && _Mysb::gptr()) { + // readable, make string view from read buffer const auto _Base = _Mysb::eback(); - _Result.assign(_Base, static_cast<_Mysize_type>(_Mysb::egptr() - _Base)); + _Result._Ptr = _Base; + _Result._Size = static_cast<_Mysize_type>(_Mysb::egptr() - _Base); + _Result._Res = _Result._Size; } + return _Result; + } +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { + _Mystr _Result(_Al); + const auto _View = _Get_buffer_view(); + if (_View._Ptr) { + _Result.assign(_View._Ptr, _View._Size); + } return _Result; } @@ -100,6 +167,72 @@ public: _Init(_Newstr.c_str(), _Newstr.size(), _Mystate); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return basic_string<_Elem, _Traits, _Alloc2>{view(), _Al}; + } + + // The buffer cannot be moved to a string directly, because + // the buffer may already be full, and the terminating char is not '\0'. + // In that case, copy the string as usual. + _NODISCARD _Mystr str() && { + _Mystr _Result{_String_constructor_rvalue_allocator_tag{}, _STD move(_Al)}; + const auto _View = _Get_buffer_view(); + // _Size cannot be larger than _Res, but it could be equal, + // because basic_stringbuf doesn't allocate for the terminating '\0'. + if (_View._Size == _View._Res) { + // 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; + } + } + _Tidy(); + return _Result; + } + + _NODISCARD _Mystr_view view() const noexcept { + const auto _View = _Get_buffer_view(); + return _Mystr_view{_View._Ptr, _View._Size}; + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Tidy(); + _Init(_Newstr.c_str(), _Newstr.size(), _Mystate); + } + + void _Str(_Mystr&& _Newstr, _Equal_allocators) { + _Tidy(); + _Pocma(_Al, _Newstr._Getal()); + _Init_string_inplace(_STD move(_Newstr), _Mystate); + } + + void _Str(_Mystr&& _Newstr, _Propagate_allocators) { + _Str(_STD move(_Newstr), _Equal_allocators{}); + } + + void _Str(_Mystr&& _Newstr, _No_propagate_allocators) { + if (_Al == _Newstr._Getal()) { + _Str(_STD move(_Newstr), _Equal_allocators{}); + } else { + _Tidy(); + _Init(_Newstr.c_str(), _Newstr.size(), _Mystate); + } + } + + void str(_Mystr&& _Newstr) { + _Str(_STD move(_Newstr), _Choose_pocma<_Alloc>{}); + } + + _NODISCARD allocator_type get_allocator() const noexcept { + return _Al; + } +#endif // _HAS_CXX20 + protected: virtual int_type overflow(int_type _Meta = _Traits::eof()) { // put an element to stream if (_Mystate & _Constant) { @@ -205,7 +338,7 @@ protected: off_type _Off, ios_base::seekdir _Way, ios_base::openmode _Mode = ios_base::in | ios_base::out) { // change position by _Off, according to _Way, _Mode const auto _Gptr_old = _Mysb::gptr(); - const auto _Pptr_old = _Mysb::pptr(); + const auto _Pptr_old = (_Mystate & _Constant) ? nullptr : _Mysb::pptr(); if (_Pptr_old && _Seekhigh < _Pptr_old) { // update high-water pointer _Seekhigh = _Pptr_old; } @@ -267,7 +400,7 @@ protected: // change position to _Pos, according to _Mode const auto _Off = static_cast(_Pos); const auto _Gptr_old = _Mysb::gptr(); - const auto _Pptr_old = _Mysb::pptr(); + const auto _Pptr_old = (_Mystate & _Constant) ? nullptr : _Mysb::pptr(); if (_Pptr_old && _Seekhigh < _Pptr_old) { // update high-water pointer _Seekhigh = _Pptr_old; } @@ -297,6 +430,8 @@ protected: protected: void _Init(const _Elem* _Ptr, _Mysize_type _Count, int _State) { // initialize buffer to [_Ptr, _Ptr + _Count), set state + _State &= ~_From_rvalue; + if (_Count > INT_MAX) { // TRANSITION, VSO-485517 _Xbad_alloc(); } @@ -328,6 +463,34 @@ protected: _Mystate = _State; } +#if _HAS_CXX20 + void _Init_string_inplace(_Mystr&& _Str, int _State) { + _State |= _From_rvalue; + + auto [_Ptr, _Size, _Res] = _Str._Release_to_buffer(_Al); + _Elem* const _Pnew = _Unfancy(_Ptr); + if (_Res != 0 && (_State & (_Noread | _Constant)) != (_Noread | _Constant)) { + // finite buffer that can be read or written, set it up + _Seekhigh = _Pnew + _Size; + auto _End_buffer = _Pnew + _Res; + + _Mysb::setp(_Pnew, (_State & (_Atend | _Append)) ? _Seekhigh : _Pnew, _End_buffer); + + if (_State & _Noread) { // maintain "_Allocated == eback() points to buffer base" invariant + _Mysb::setg(_Pnew, nullptr, _Pnew); + } else { + _Mysb::setg(_Pnew, _Pnew, _Seekhigh); + } + + _State |= _Allocated; + } else { + _Seekhigh = nullptr; + } + + _Mystate = _State; + } +#endif // _HAS_CXX20 + void _Tidy() noexcept { // discard any allocated buffer and clear pointers if (_Mystate & _Allocated) { _Al.deallocate(_Ptr_traits::pointer_to(*_Mysb::eback()), @@ -369,13 +532,15 @@ private: return _State; } + // TRANSITION, ABI, see GH-938 _Elem* _Seekhigh; // the high-water pointer in character array int _Mystate; // the stream state allocator_type _Al; // the allocator object }; template -void swap(basic_stringbuf<_Elem, _Traits, _Alloc>& _Left, basic_stringbuf<_Elem, _Traits, _Alloc>& _Right) { +void swap(basic_stringbuf<_Elem, _Traits, _Alloc>& _Left, basic_stringbuf<_Elem, _Traits, _Alloc>& _Right) noexcept +/* strengthened */ { _Left.swap(_Right); } @@ -388,6 +553,10 @@ public: using _Mysb = basic_stringbuf<_Elem, _Traits, _Alloc>; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_istringstream() : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(ios_base::in) {} explicit basic_istringstream(ios_base::openmode _Mode) @@ -396,6 +565,27 @@ public: explicit basic_istringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::in) : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::in) {} +#if _HAS_CXX20 + basic_istringstream(ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Mode | ios_base::in, _Al) {} + + explicit basic_istringstream(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::in) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_STD move(_Str), _Mode | ios_base::in) {} + + template + basic_istringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, ios_base::in, _Al) {} + + template + basic_istringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::in, _Al) {} + + template , int> = 0> + explicit basic_istringstream( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::in) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::in) {} +#endif // _HAS_CXX20 + basic_istringstream(basic_istringstream&& _Right) : _Mybase(_STD addressof(_Stringbuffer)) { _Assign_rv(_STD move(_Right)); } @@ -428,7 +618,12 @@ public: return const_cast<_Mysb*>(_STD addressof(_Stringbuffer)); } - _NODISCARD _Mystr str() const { +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { return _Stringbuffer.str(); } @@ -436,6 +631,30 @@ public: _Stringbuffer.str(_Newstr); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return _Stringbuffer.str(_Al); + } + + _NODISCARD _Mystr str() && { + return _STD move(_Stringbuffer).str(); + } + + _NODISCARD _Mystr_view view() const noexcept { + return _Stringbuffer.view(); + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Stringbuffer.str(_Newstr); + } + + void str(_Mystr&& _Newstr) { + _Stringbuffer.str(_STD move(_Newstr)); + } +#endif // _HAS_CXX20 + private: _Mysb _Stringbuffer; }; @@ -454,6 +673,10 @@ public: using _Mysb = basic_stringbuf<_Elem, _Traits, _Alloc>; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_ostringstream() : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(ios_base::out) {} explicit basic_ostringstream(ios_base::openmode _Mode) @@ -462,6 +685,27 @@ public: explicit basic_ostringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::out) : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::out) {} +#if _HAS_CXX20 + basic_ostringstream(ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Mode | ios_base::out, _Al) {} + + explicit basic_ostringstream(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_STD move(_Str), _Mode | ios_base::out) {} + + template + basic_ostringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, ios_base::out, _Al) {} + + template + basic_ostringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::out, _Al) {} + + template , int> = 0> + explicit basic_ostringstream( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::out) {} +#endif // _HAS_CXX20 + basic_ostringstream(basic_ostringstream&& _Right) : _Mybase(_STD addressof(_Stringbuffer)) { _Assign_rv(_STD move(_Right)); } @@ -494,7 +738,12 @@ public: return const_cast<_Mysb*>(_STD addressof(_Stringbuffer)); } - _NODISCARD _Mystr str() const { +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { return _Stringbuffer.str(); } @@ -502,6 +751,30 @@ public: _Stringbuffer.str(_Newstr); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return _Stringbuffer.str(_Al); + } + + _NODISCARD _Mystr str() && { + return _STD move(_Stringbuffer).str(); + } + + _NODISCARD _Mystr_view view() const noexcept { + return _Stringbuffer.view(); + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Stringbuffer.str(_Newstr); + } + + void str(_Mystr&& _Newstr) { + _Stringbuffer.str(_STD move(_Newstr)); + } +#endif // _HAS_CXX20 + private: _Mysb _Stringbuffer; }; @@ -526,6 +799,10 @@ public: using _Mysb = basic_stringbuf<_Elem, _Traits, _Alloc>; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_stringstream() : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(ios_base::in | ios_base::out) {} explicit basic_stringstream(ios_base::openmode _Mode) @@ -534,6 +811,27 @@ public: explicit basic_stringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode) {} +#if _HAS_CXX20 + basic_stringstream(ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Mode, _Al) {} + + explicit basic_stringstream(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_STD move(_Str), _Mode) {} + + template + basic_stringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, ios_base::in | ios_base::out, _Al) {} + + template + basic_stringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode, _Al) {} + + template , int> = 0> + explicit basic_stringstream( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode) {} +#endif // _HAS_CXX20 + basic_stringstream(basic_stringstream&& _Right) : _Mybase(_STD addressof(_Stringbuffer)) { _Assign_rv(_STD move(_Right)); } @@ -566,7 +864,12 @@ public: return const_cast<_Mysb*>(_STD addressof(_Stringbuffer)); } - _NODISCARD _Mystr str() const { +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { return _Stringbuffer.str(); } @@ -574,6 +877,30 @@ public: _Stringbuffer.str(_Newstr); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return _Stringbuffer.str(_Al); + } + + _NODISCARD _Mystr str() && { + return _STD move(_Stringbuffer).str(); + } + + _NODISCARD _Mystr_view view() const noexcept { + return _Stringbuffer.view(); + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Stringbuffer.str(_Newstr); + } + + void str(_Mystr&& _Newstr) { + _Stringbuffer.str(_STD move(_Newstr)); + } +#endif // _HAS_CXX20 + private: _Mysb _Stringbuffer; }; diff --git a/stl/inc/xstring b/stl/inc/xstring index 00a4d2354b1..80355987c2c 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -2272,6 +2272,11 @@ struct _String_constructor_concat_tag { explicit _String_constructor_concat_tag() = default; }; +struct _String_constructor_rvalue_allocator_tag { + // tag to select constructors used by basic_stringbuf's rvalue str() + explicit _String_constructor_rvalue_allocator_tag() = default; +}; + [[noreturn]] inline void _Xlen_string() { _Xlength_error("string too long"); } @@ -2280,6 +2285,7 @@ template , class _Alloc = alloca class basic_string { // null-terminated transparent array of elements private: friend _Tidy_deallocate_guard; + friend basic_stringbuf<_Elem, _Traits, _Alloc>; using _Alty = _Rebind_alloc_t<_Alloc, _Elem>; using _Alty_traits = allocator_traits<_Alty>; @@ -2633,6 +2639,14 @@ public: } #endif // _HAS_CXX17 +#if _HAS_CXX20 + basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al) + : _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) { + _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); + _Tidy_init(); + } +#endif // _HAS_CXX20 + private: void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { _Tidy_deallocate(); @@ -2662,6 +2676,51 @@ private: } public: +#if _HAS_CXX20 + bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { + // Move assign from a buffer, used 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; + } else { + _Traits::copy(_My_data._Bx._Buf, _Right, _Res); + _My_data._Myres = _BUF_SIZE - 1; + return false; + } + } + + // No instance of this type can exist where an exception may be thrown. + struct _Released_buffer { + pointer _Ptr; + size_type _Size; + size_type _Res; + }; + + _NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) { + // Release to a buffer, or allocate a new one if in small string mode + _Released_buffer _Result; + auto& _My_data = _Mypair._Myval2; + _Result._Size = _My_data._Mysize; + if (_My_data._Large_string_engaged()) { + _Result._Ptr = _My_data._Bx._Ptr; + _Result._Res = _My_data._Myres + 1; + } else { + // use _BUF_SIZE + 1 to avoid SSO, if the buffer is assigned back + _Result._Ptr = _Al.allocate(_BUF_SIZE + 1); + _Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE); + _Result._Res = _BUF_SIZE + 1; + } + _My_data._Orphan_all(); + _Tidy_init(); + return _Result; + } +#endif // _HAS_CXX20 + basic_string& operator=(basic_string&& _Right) noexcept(noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { if (this != _STD addressof(_Right)) { _Move_assign(_Right, _Choose_pocma<_Alty>{}); diff --git a/stl/inc/xutility b/stl/inc/xutility index f484b23b1f9..9f1c687ad01 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1441,6 +1441,11 @@ using _Enable_if_execution_policy_t = typename remove_reference_t<_ExPo>::_Stand #endif // _HAS_CXX17 +#if _HAS_CXX20 +template +concept _Allocator = _Is_allocator<_Ty>::value; +#endif // _HAS_CXX20 + // FUNCTION TEMPLATE _Idl_distance template _NODISCARD constexpr auto _Idl_distance(const _Iter& _First, const _Iter& _Last) { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 28011748cb5..e37d8daf036 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -140,6 +140,7 @@ // P0339R6 polymorphic_allocator<> // P0356R5 bind_front() // P0357R3 Supporting Incomplete Types In reference_wrapper +// P0408R7 Efficient Access To basic_stringbuf's Buffer // P0415R1 constexpr For (Again) // P0439R0 enum class memory_order // P0457R2 starts_with()/ends_with() For basic_string/basic_string_view diff --git a/tests/std/test.lst b/tests/std/test.lst index acdf8d9b478..014d2c798d4 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -229,6 +229,7 @@ tests\P0339R6_polymorphic_allocator tests\P0355R7_calendars_and_time_zones_io tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper +tests\P0408R7_efficient_access_to_stringbuf_buffer tests\P0414R2_shared_ptr_for_arrays tests\P0415R1_constexpr_complex tests\P0426R1_constexpr_char_traits diff --git a/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/env.lst b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/env.lst new file mode 100644 index 00000000000..e5b00aee0d4 --- /dev/null +++ b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_winsdk_matrix.lst diff --git a/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/test.cpp b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/test.cpp new file mode 100644 index 00000000000..84d4474aa25 --- /dev/null +++ b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/test.cpp @@ -0,0 +1,302 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +constexpr char empty_string[] = ""; +constexpr char small_string[] = "a"; +constexpr char large_string[] = + "This is a long long long long long long long long string to avoid small string optimization."; + +template +struct test_rvalue { + void operator()(const string& init_value) { + string buffer{init_value}; + size_t res = buffer.capacity(); + Stream stream{move(buffer)}; + assert(stream.view() == init_value); + assert(stream.str() == init_value); + assert(stream.view() == init_value); + assert(stream.rdbuf()->get_allocator() == init_value.get_allocator()); + // Move out the buffer, the underlying buffer should be empty. + buffer = move(stream).str(); + if (buffer == large_string) { + // stream doesn't actually have space for a null-terminator + assert(buffer.capacity() == res); + } else { + assert(buffer.capacity() == res + 1); + } + assert(buffer == init_value); + assert(stream.view().empty()); + assert(stream.str().empty()); + // Move in the buffer string + stream.str(move(buffer)); + assert(stream.view() == init_value); + assert(stream.str() == init_value); + assert(stream.rdbuf()->get_allocator() == init_value.get_allocator()); + // Move to another stream + Stream stream2 = move(stream); + assert(stream.view().empty()); + assert(stream2.view() == init_value); + } +}; + +template +struct test_allocator { + void operator()(const pmr::string& init_value) { + Stream stream{init_value}; + assert(stream.view() == init_value); + assert(stream.str(init_value.get_allocator()) == init_value); + assert(stream.str() == string_view{init_value}); + assert(move(stream).str() == string_view{init_value}); + assert(stream.view().empty()); + stream.str(init_value); + assert(stream.view() == init_value); + assert(stream.str(init_value.get_allocator()) == init_value); + assert(stream.str() == string_view{init_value}); + } +}; + +template +struct test_pmr_allocator { + pmr::polymorphic_allocator alloc; + + void operator()(const pmr::string& init_value) { + Stream stream{init_value, alloc}; + assert(stream.rdbuf()->get_allocator() != init_value.get_allocator()); + { + pmr::string new_str = stream.str(init_value.get_allocator()); + assert(new_str == init_value); + assert(new_str.get_allocator() == init_value.get_allocator()); + } + { + pmr::string new_str = stream.str(); + assert(new_str == init_value); + assert(new_str.get_allocator() != init_value.get_allocator()); + } + stream.str(init_value); + assert(stream.rdbuf()->get_allocator() != init_value.get_allocator()); + { + pmr::string new_str = stream.str(init_value.get_allocator()); + assert(new_str == init_value); + assert(new_str.get_allocator() == init_value.get_allocator()); + } + { + pmr::string new_str = stream.str(); + assert(new_str == init_value); + assert(new_str.get_allocator() != init_value.get_allocator()); + } + Stream stream2{init_value, init_value.get_allocator()}; + assert(stream2.rdbuf()->get_allocator() == init_value.get_allocator()); + } +}; + +template +void run_test_util() { + Test test{}; + test(empty_string); + test(small_string); + test(large_string); +} + +template