зеркало из https://github.com/microsoft/STL.git
Lifetime cleanups for `basic_string` (#4047)
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Родитель
a9123e8383
Коммит
8d6922ae46
|
@ -2287,6 +2287,19 @@ public:
|
||||||
value_type _Buf[_BUF_SIZE];
|
value_type _Buf[_BUF_SIZE];
|
||||||
pointer _Ptr;
|
pointer _Ptr;
|
||||||
char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr)
|
char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr)
|
||||||
|
|
||||||
|
_CONSTEXPR20 void _Switch_to_buf() noexcept {
|
||||||
|
_STD _Destroy_in_place(_Ptr);
|
||||||
|
|
||||||
|
#if _HAS_CXX20
|
||||||
|
// start the lifetime of the array elements
|
||||||
|
if (_STD is_constant_evaluated()) {
|
||||||
|
for (size_type _Idx = 0; _Idx < _BUF_SIZE; ++_Idx) {
|
||||||
|
_Buf[_Idx] = value_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // _HAS_CXX20
|
||||||
|
}
|
||||||
};
|
};
|
||||||
_Bxty _Bx;
|
_Bxty _Bx;
|
||||||
|
|
||||||
|
@ -2484,13 +2497,11 @@ private:
|
||||||
public:
|
public:
|
||||||
_CONSTEXPR20
|
_CONSTEXPR20
|
||||||
basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) {
|
basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) {
|
||||||
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
|
_Construct_empty();
|
||||||
_Tidy_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_CONSTEXPR20 explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) {
|
_CONSTEXPR20 explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) {
|
||||||
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
|
_Construct_empty();
|
||||||
_Tidy_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_CONSTEXPR20 basic_string(const basic_string& _Right)
|
_CONSTEXPR20 basic_string(const basic_string& _Right)
|
||||||
|
@ -2578,8 +2589,7 @@ public:
|
||||||
auto _UFirst = _Get_unwrapped(_First);
|
auto _UFirst = _Get_unwrapped(_First);
|
||||||
auto _ULast = _Get_unwrapped(_Last);
|
auto _ULast = _Get_unwrapped(_Last);
|
||||||
if (_UFirst == _ULast) {
|
if (_UFirst == _ULast) {
|
||||||
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
|
_Construct_empty();
|
||||||
_Tidy_init();
|
|
||||||
} else {
|
} else {
|
||||||
if constexpr (_Is_elem_cptr<decltype(_UFirst)>::value) {
|
if constexpr (_Is_elem_cptr<decltype(_UFirst)>::value) {
|
||||||
_Construct<_Construct_strategy::_From_ptr>(
|
_Construct<_Construct_strategy::_From_ptr>(
|
||||||
|
@ -2630,6 +2640,19 @@ private:
|
||||||
_Al.deallocate(_Old_ptr, _Capacity + 1); // +1 for null terminator
|
_Al.deallocate(_Old_ptr, _Capacity + 1); // +1 for null terminator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_CONSTEXPR20 void _Construct_empty() {
|
||||||
|
auto& _My_data = _Mypair._Myval2;
|
||||||
|
_My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
|
||||||
|
|
||||||
|
// initialize basic_string data members
|
||||||
|
_My_data._Mysize = 0;
|
||||||
|
_My_data._Myres = _Small_string_capacity;
|
||||||
|
_My_data._Activate_SSO_buffer();
|
||||||
|
|
||||||
|
// the _Traits::assign is last so the codegen doesn't think the char write can alias this
|
||||||
|
_Traits::assign(_My_data._Bx._Buf[0], _Elem());
|
||||||
|
}
|
||||||
|
|
||||||
enum class _Construct_strategy : uint8_t { _From_char, _From_ptr, _From_string };
|
enum class _Construct_strategy : uint8_t { _From_char, _From_ptr, _From_string };
|
||||||
|
|
||||||
template <_Construct_strategy _Strat, class _Char_or_ptr>
|
template <_Construct_strategy _Strat, class _Char_or_ptr>
|
||||||
|
@ -2918,8 +2941,7 @@ public:
|
||||||
basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al)
|
basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al)
|
||||||
: _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) {
|
: _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) {
|
||||||
// Used exclusively by basic_stringbuf
|
// Used exclusively by basic_stringbuf
|
||||||
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
|
_Construct_empty();
|
||||||
_Tidy_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_NODISCARD bool _Move_assign_from_buffer(
|
_NODISCARD bool _Move_assign_from_buffer(
|
||||||
|
@ -2958,10 +2980,13 @@ public:
|
||||||
_Released_buffer _Result;
|
_Released_buffer _Result;
|
||||||
auto& _My_data = _Mypair._Myval2;
|
auto& _My_data = _Mypair._Myval2;
|
||||||
_Result._Size = _My_data._Mysize;
|
_Result._Size = _My_data._Mysize;
|
||||||
|
_My_data._Orphan_all();
|
||||||
_ASAN_STRING_REMOVE(*this);
|
_ASAN_STRING_REMOVE(*this);
|
||||||
if (_My_data._Large_mode_engaged()) {
|
if (_My_data._Large_mode_engaged()) {
|
||||||
_Result._Ptr = _My_data._Bx._Ptr;
|
_Result._Ptr = _My_data._Bx._Ptr;
|
||||||
_Result._Actual_allocation_size = _My_data._Myres + 1;
|
_Result._Actual_allocation_size = _My_data._Myres + 1;
|
||||||
|
|
||||||
|
_My_data._Bx._Switch_to_buf();
|
||||||
} else {
|
} else {
|
||||||
// use _Least_allocation_size to avoid small mode, if the buffer is assigned back
|
// use _Least_allocation_size to avoid small mode, if the buffer is assigned back
|
||||||
size_type _Allocated = _Least_allocation_size;
|
size_type _Allocated = _Least_allocation_size;
|
||||||
|
@ -2969,8 +2994,9 @@ public:
|
||||||
_Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE);
|
_Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE);
|
||||||
_Result._Actual_allocation_size = _Allocated;
|
_Result._Actual_allocation_size = _Allocated;
|
||||||
}
|
}
|
||||||
_My_data._Orphan_all();
|
_My_data._Mysize = 0;
|
||||||
_Tidy_init();
|
_My_data._Myres = _Small_string_capacity;
|
||||||
|
_Traits::assign(_My_data._Bx._Buf[0], _Elem());
|
||||||
return _Result;
|
return _Result;
|
||||||
}
|
}
|
||||||
#endif // _HAS_CXX20
|
#endif // _HAS_CXX20
|
||||||
|
@ -3038,27 +3064,34 @@ private:
|
||||||
const auto _Right_data_mem =
|
const auto _Right_data_mem =
|
||||||
reinterpret_cast<const unsigned char*>(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset;
|
reinterpret_cast<const unsigned char*>(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset;
|
||||||
_CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
|
_CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
|
||||||
_Right._Tidy_init();
|
|
||||||
|
_Right_data._Mysize = 0;
|
||||||
|
_Right_data._Myres = _Small_string_capacity;
|
||||||
|
_Right_data._Activate_SSO_buffer();
|
||||||
|
_Traits::assign(_Right_data._Bx._Buf[0], _Elem());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // !defined(_INSERT_STRING_ANNOTATION)
|
#endif // !defined(_INSERT_STRING_ANNOTATION)
|
||||||
|
|
||||||
if (_Right_data._Large_mode_engaged()) { // steal buffer
|
if (_Right_data._Large_mode_engaged()) { // steal buffer
|
||||||
_Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr);
|
|
||||||
_Swap_proxy_and_iterators(_Right);
|
_Swap_proxy_and_iterators(_Right);
|
||||||
|
|
||||||
_Destroy_in_place(_Right_data._Bx._Ptr);
|
_Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr);
|
||||||
|
_Right_data._Bx._Switch_to_buf();
|
||||||
} else { // copy small string buffer
|
} else { // copy small string buffer
|
||||||
|
_Right_data._Orphan_all();
|
||||||
|
|
||||||
_My_data._Activate_SSO_buffer();
|
_My_data._Activate_SSO_buffer();
|
||||||
_Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1);
|
_Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1);
|
||||||
_Right_data._Orphan_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_My_data._Myres = _Right_data._Myres;
|
_My_data._Myres = _Right_data._Myres;
|
||||||
_My_data._Mysize = _Right_data._Mysize;
|
_My_data._Mysize = _Right_data._Mysize;
|
||||||
|
|
||||||
_Right._Tidy_init();
|
_Right_data._Mysize = 0;
|
||||||
|
_Right_data._Myres = _Small_string_capacity;
|
||||||
|
_Traits::assign(_Right_data._Bx._Buf[0], _Elem());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _HAS_CXX23
|
#if _HAS_CXX23
|
||||||
|
@ -4252,9 +4285,7 @@ public:
|
||||||
static _CONSTEXPR20 void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) noexcept {
|
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
|
// exchange a string in large mode with one in small mode
|
||||||
const pointer _Ptr = _Starts_large._Bx._Ptr;
|
const pointer _Ptr = _Starts_large._Bx._Ptr;
|
||||||
_Destroy_in_place(_Starts_large._Bx._Ptr);
|
_Starts_large._Bx._Switch_to_buf();
|
||||||
|
|
||||||
_Starts_large._Activate_SSO_buffer();
|
|
||||||
_Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE);
|
_Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE);
|
||||||
|
|
||||||
_Construct_in_place(_Starts_small._Bx._Ptr, _Ptr);
|
_Construct_in_place(_Starts_small._Bx._Ptr, _Ptr);
|
||||||
|
@ -4802,10 +4833,9 @@ private:
|
||||||
_My_data._Orphan_all();
|
_My_data._Orphan_all();
|
||||||
_ASAN_STRING_REMOVE(*this);
|
_ASAN_STRING_REMOVE(*this);
|
||||||
const pointer _Ptr = _My_data._Bx._Ptr;
|
const pointer _Ptr = _My_data._Bx._Ptr;
|
||||||
auto& _Al = _Getal();
|
_My_data._Bx._Switch_to_buf();
|
||||||
_Destroy_in_place(_My_data._Bx._Ptr);
|
|
||||||
_My_data._Activate_SSO_buffer();
|
|
||||||
_Traits::copy(_My_data._Bx._Buf, _Unfancy(_Ptr), _My_data._Mysize + 1);
|
_Traits::copy(_My_data._Bx._Buf, _Unfancy(_Ptr), _My_data._Mysize + 1);
|
||||||
|
auto& _Al = _Getal();
|
||||||
_Deallocate_for_capacity(_Al, _Ptr, _My_data._Myres);
|
_Deallocate_for_capacity(_Al, _Ptr, _My_data._Myres);
|
||||||
_My_data._Myres = _Small_string_capacity;
|
_My_data._Myres = _Small_string_capacity;
|
||||||
}
|
}
|
||||||
|
@ -4815,27 +4845,14 @@ private:
|
||||||
_Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _New_size], _Elem());
|
_Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _New_size], _Elem());
|
||||||
}
|
}
|
||||||
|
|
||||||
_CONSTEXPR20 void _Tidy_init() noexcept {
|
|
||||||
// initialize basic_string data members
|
|
||||||
auto& _My_data = _Mypair._Myval2;
|
|
||||||
_My_data._Mysize = 0;
|
|
||||||
_My_data._Myres = _Small_string_capacity;
|
|
||||||
_My_data._Activate_SSO_buffer();
|
|
||||||
|
|
||||||
// the _Traits::assign is last so the codegen doesn't think the char write can alias this
|
|
||||||
_Traits::assign(_My_data._Bx._Buf[0], _Elem());
|
|
||||||
}
|
|
||||||
|
|
||||||
_CONSTEXPR20 void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage
|
_CONSTEXPR20 void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage
|
||||||
auto& _My_data = _Mypair._Myval2;
|
auto& _My_data = _Mypair._Myval2;
|
||||||
_My_data._Orphan_all();
|
_My_data._Orphan_all();
|
||||||
if (_My_data._Large_mode_engaged()) {
|
if (_My_data._Large_mode_engaged()) {
|
||||||
_ASAN_STRING_REMOVE(*this);
|
_ASAN_STRING_REMOVE(*this);
|
||||||
const pointer _Ptr = _My_data._Bx._Ptr;
|
|
||||||
auto& _Al = _Getal();
|
auto& _Al = _Getal();
|
||||||
_Destroy_in_place(_My_data._Bx._Ptr);
|
_Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres);
|
||||||
_My_data._Activate_SSO_buffer();
|
_My_data._Bx._Switch_to_buf();
|
||||||
_Deallocate_for_capacity(_Al, _Ptr, _My_data._Myres);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_My_data._Mysize = 0;
|
_My_data._Mysize = 0;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <crtdbg.h>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -971,6 +973,20 @@ _CONSTEXPR20 void test_string_swap(const size_t id1, const size_t id2) {
|
||||||
assert(dst.get_allocator().id() == id1);
|
assert(dst.get_allocator().id() == id1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _HAS_CXX20
|
||||||
|
void test_string_move_to_stringbuf() {
|
||||||
|
// GH-4047 fixed a bug where basic_string forgets to destroy the pointer before switching to small
|
||||||
|
// mode. This will turn problematic if the pointer is non-trivial.
|
||||||
|
assert(!_CrtDumpMemoryLeaks());
|
||||||
|
{
|
||||||
|
using Alloc = StationaryAlloc<char>;
|
||||||
|
basic_string<char, char_traits<char>, Alloc> str(50, '0', Alloc(10));
|
||||||
|
basic_stringbuf<char, char_traits<char>, Alloc> strbuf(move(str));
|
||||||
|
}
|
||||||
|
assert(!_CrtDumpMemoryLeaks());
|
||||||
|
}
|
||||||
|
#endif // _HAS_CXX20
|
||||||
|
|
||||||
_CONSTEXPR20 bool test_string() {
|
_CONSTEXPR20 bool test_string() {
|
||||||
test_string_copy_ctor();
|
test_string_copy_ctor();
|
||||||
|
|
||||||
|
@ -1002,6 +1018,11 @@ _CONSTEXPR20 bool test_string() {
|
||||||
test_string_swap<SwapAlloc<char32_t>>(11, 22); // POCS, non-equal allocators
|
test_string_swap<SwapAlloc<char32_t>>(11, 22); // POCS, non-equal allocators
|
||||||
test_string_swap<SwapEqualAlloc<char32_t>>(11, 22); // POCS, always-equal allocators
|
test_string_swap<SwapEqualAlloc<char32_t>>(11, 22); // POCS, always-equal allocators
|
||||||
|
|
||||||
|
#if _HAS_CXX20
|
||||||
|
if (!is_constant_evaluated()) {
|
||||||
|
test_string_move_to_stringbuf();
|
||||||
|
}
|
||||||
|
#endif // _HAS_CXX20
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче