Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<xstring>: Use memmove in construction of basic_string when the source is a suitable contiguous range #4073

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 47 additions & 20 deletions stl/inc/xstring
Original file line number Diff line number Diff line change
Expand Up @@ -2722,35 +2722,62 @@ private:
}

_Tidy_deallocate_guard<basic_string> _Guard{this};
for (; _First != _Last; ++_First) {
if constexpr (!is_same_v<_Size, size_type>) {
if (_My_data._Mysize == _My_data._Myres) { // Need to grow
if (_My_data._Mysize == max_size()) {
_Xlen_string(); // result too long
}

_Elem* const _Old_ptr = _My_data._Myptr();
size_type _New_capacity = _Calculate_growth(_My_data._Mysize + 1);
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws
constexpr bool _Can_construct_by_memcpy = _Is_specialization_v<_Traits, char_traits> && _Is_EcharT<_Elem>
&& _Iterator_is_contiguous<_Iter> && !_Iterator_is_volatile<_Iter>
&& is_integral_v<_Iter_value_t<_Iter>>
&& sizeof(_Iter_value_t<_Iter>) == sizeof(_Elem);
frederick-vs-ja marked this conversation as resolved.
Show resolved Hide resolved

if constexpr (_Can_construct_by_memcpy) {
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Size, size_type>);
frederick-vs-ja marked this conversation as resolved.
Show resolved Hide resolved

_Traits::copy(_Unfancy(_New_ptr), _Old_ptr, _My_data._Mysize);
if (_My_data._Large_mode_engaged()) { // Need to deallocate old storage
_Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres);
_My_data._Bx._Ptr = _New_ptr;
} else {
_Construct_in_place(_My_data._Bx._Ptr, _New_ptr);
const auto _Data = _My_data._Myptr();
const auto _Src_data = _STD _To_address(_First);

#if _HAS_CXX20
if (_STD is_constant_evaluated()) {
for (size_type _Idx = 0; _Idx != _Count; ++_Idx) {
_Data[_Idx] = static_cast<_Elem>(_Src_data[_Idx]);
}
} else
#endif // _HAS_CXX20
{
_CSTD memcpy(_Data, _Src_data, _Count * sizeof(_Elem));
}
_My_data._Mysize = _Count;
_Data[_Count] = _Elem();
} else {
for (; _First != _Last; ++_First) {
if constexpr (!is_same_v<_Size, size_type>) {
if (_My_data._Mysize == _My_data._Myres) { // Need to grow
if (_My_data._Mysize == max_size()) {
_Xlen_string(); // result too long
}

_Elem* const _Old_ptr = _My_data._Myptr();
size_type _New_capacity = _Calculate_growth(_My_data._Mysize + 1);
const pointer _New_ptr = _Allocate_for_capacity(_Al, _New_capacity); // throws

_Traits::copy(_Unfancy(_New_ptr), _Old_ptr, _My_data._Mysize);
if (_My_data._Large_mode_engaged()) { // Need to deallocate old storage
_Deallocate_for_capacity(_Al, _My_data._Bx._Ptr, _My_data._Myres);
_My_data._Bx._Ptr = _New_ptr;
} else {
_Construct_in_place(_My_data._Bx._Ptr, _New_ptr);
}
_My_data._Myres = _New_capacity;
}
_My_data._Myres = _New_capacity;
}

_Elem* const _Ptr = _My_data._Myptr();
_Traits::assign(_Ptr[_My_data._Mysize], *_First);
++_My_data._Mysize;
}

_Elem* const _Ptr = _My_data._Myptr();
_Traits::assign(_Ptr[_My_data._Mysize], *_First);
++_My_data._Mysize;
_Traits::assign(_Ptr[_My_data._Mysize], _Elem());
}

_Elem* const _Ptr = _My_data._Myptr();
_Traits::assign(_Ptr[_My_data._Mysize], _Elem());
_ASAN_STRING_CREATE(*this);
_Guard._Target = nullptr;
_Proxy._Release();
Expand Down
252 changes: 242 additions & 10 deletions tests/std/tests/P1206R7_string_from_range/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <forward_list>
#include <ranges>
#include <span>
Expand Down Expand Up @@ -44,6 +46,12 @@ constexpr bool test_string(Rng&& rng, const T* expected) {
static constexpr char hw[] = "Hello, world!";
static constexpr auto span_hw = span{hw}.first<span{hw}.size() - 1>();

static constexpr signed char hw_s[] = "Hello, world!";
static constexpr auto span_hw_s = span{hw_s}.first<span{hw_s}.size() - 1>();

static constexpr unsigned char hw_u[] = "Hello, world!";
static constexpr auto span_hw_u = span{hw_u}.first<span{hw_u}.size() - 1>();

struct string_instantiator {
template <ranges::input_range R>
static void call() {
Expand All @@ -52,9 +60,76 @@ struct string_instantiator {
}
};

#ifdef __cpp_char8_t
static constexpr char8_t hw_u8[] = u8"Hello, world!";
static constexpr auto span_hw_u8 = span{hw_u8}.first<span{hw_u8}.size() - 1>();

struct u8string_instantiator {
template <ranges::input_range R>
static void call() {
test_string(R{span_hw_u8}, hw_u8);
STATIC_ASSERT(test_string(R{span_hw_u8}, hw_u8));
}
};
#endif // defined(__cpp_char8_t)

static constexpr char16_t hw_u16[] = u"Hello, world!";
static constexpr auto span_hw_u16 = span{hw_u16}.first<span{hw_u16}.size() - 1>();

static constexpr int_least16_t hw_u16s[]{
u'H', u'e', u'l', u'l', u'o', u',', u' ', u'w', u'o', u'r', u'l', u'd', u'!', u'\0'};
static constexpr auto span_hw_u16s = span{hw_u16s}.first<span{hw_u16s}.size() - 1>();

static constexpr uint_least16_t hw_u16u[]{
u'H', u'e', u'l', u'l', u'o', u',', u' ', u'w', u'o', u'r', u'l', u'd', u'!', u'\0'};
static constexpr auto span_hw_u16u = span{hw_u16u}.first<span{hw_u16u}.size() - 1>();

struct u16string_instantiator {
template <ranges::input_range R>
static void call() {
test_string(R{span_hw_u16}, hw_u16);
STATIC_ASSERT(test_string(R{span_hw_u16}, hw_u16));
}
};

static constexpr char32_t hw_u32[] = U"Hello, world!";
static constexpr auto span_hw_u32 = span{hw_u32}.first<span{hw_u32}.size() - 1>();

static constexpr int_least32_t hw_u32s[]{
U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!', U'\0'};
static constexpr auto span_hw_u32s = span{hw_u32s}.first<span{hw_u32s}.size() - 1>();

static constexpr uint_least32_t hw_u32u[]{
U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!', U'\0'};
static constexpr auto span_hw_u32u = span{hw_u32u}.first<span{hw_u32u}.size() - 1>();

static constexpr long hw_slong[]{U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!', U'\0'};
static constexpr auto span_hw_slong = span{hw_slong}.first<span{hw_slong}.size() - 1>();

static constexpr unsigned long hw_ulong[]{
U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!', U'\0'};
static constexpr auto span_hw_ulong = span{hw_ulong}.first<span{hw_ulong}.size() - 1>();

struct u32string_instantiator {
template <ranges::input_range R>
static void call() {
test_string(R{span_hw_u32}, hw_u32);
STATIC_ASSERT(test_string(R{span_hw_u32}, hw_u32));
}
};

using swchar_t = make_signed_t<wchar_t>;
using uwchar_t = make_unsigned_t<wchar_t>;

static constexpr wchar_t whw[] = L"Hello, world!";
static constexpr auto span_whw = span{whw}.first<span{whw}.size() - 1>();

static constexpr swchar_t whw_s[]{L'H', L'e', L'l', L'l', L'o', L',', L' ', L'w', L'o', L'r', L'l', L'd', L'!', L'\0'};
static constexpr auto span_whw_s = span{whw_s}.first<span{whw_s}.size() - 1>();

static constexpr uwchar_t whw_u[]{L'H', L'e', L'l', L'l', L'o', L',', L' ', L'w', L'o', L'r', L'l', L'd', L'!', L'\0'};
static constexpr auto span_whw_u = span{whw_u}.first<span{whw_u}.size() - 1>();

struct wstring_instantiator {
template <ranges::input_range R>
static void call() {
Expand All @@ -71,7 +146,32 @@ using move_only_view = test::range<Category, const CharT, test::Sized{is_random}

constexpr bool test_copyable_views() {
test_string(span_hw, hw);
test_string(span_hw_s, hw);
test_string(span_hw_u, hw);
#ifdef __cpp_char8_t
test_string(span_hw_u8, hw);

test_string(span_hw_u8, hw_u8);
test_string(span_hw, hw_u8);
test_string(span_hw_s, hw_u8);
test_string(span_hw_u, hw_u8);
#endif // defined(__cpp_char8_t)

test_string(span_hw_u16, hw_u16);
test_string(span_hw_u16s, hw_u16);
test_string(span_hw_u16u, hw_u16);
test_string(span_whw, hw_u16);

test_string(span_hw_u32, hw_u32);
test_string(span_hw_u32s, hw_u32);
test_string(span_hw_u32u, hw_u32);
test_string(span_hw_slong, hw_u32);
test_string(span_hw_ulong, hw_u32);

test_string(span_whw, whw);
test_string(span_whw_s, whw);
test_string(span_whw_u, whw);
test_string(span_hw_u16, whw);

return true;
}
Expand All @@ -96,21 +196,130 @@ constexpr bool test_move_only_views() {
return true;
}

static constexpr char simple_hw[]{'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'};
static constexpr signed char simple_hw_s[]{'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'};
static constexpr unsigned char simple_hw_u[]{'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'};
#ifdef __cpp_char8_t
static constexpr char8_t simple_hw_u8[]{
u8'H', u8'e', u8'l', u8'l', u8'o', u8',', u8' ', u8'w', u8'o', u8'r', u8'l', u8'd', u8'!'};
#endif // defined(__cpp_char8_t)

static constexpr char16_t simple_hw_u16[]{u'H', u'e', u'l', u'l', u'o', u',', u' ', u'w', u'o', u'r', u'l', u'd', u'!'};
static constexpr int_least16_t simple_hw_u16s[]{
u'H', u'e', u'l', u'l', u'o', u',', u' ', u'w', u'o', u'r', u'l', u'd', u'!'};
static constexpr uint_least16_t simple_hw_u16u[]{
u'H', u'e', u'l', u'l', u'o', u',', u' ', u'w', u'o', u'r', u'l', u'd', u'!'};

static constexpr char32_t simple_hw_u32[]{U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!'};
static constexpr int_least32_t simple_hw_u32s[]{
U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!'};
static constexpr uint_least32_t simple_hw_u32u[]{
U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!'};
static constexpr long simple_hw_slong[]{U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!'};
static constexpr unsigned long simple_hw_ulong[]{
U'H', U'e', U'l', U'l', U'o', U',', U' ', U'w', U'o', U'r', U'l', U'd', U'!'};

static constexpr wchar_t simple_whw[]{L'H', L'e', L'l', L'l', L'o', L',', L' ', L'w', L'o', L'r', L'l', L'd', L'!'};
static constexpr swchar_t simple_whw_s[]{L'H', L'e', L'l', L'l', L'o', L',', L' ', L'w', L'o', L'r', L'l', L'd', L'!'};
static constexpr uwchar_t simple_whw_u[]{L'H', L'e', L'l', L'l', L'o', L',', L' ', L'w', L'o', L'r', L'l', L'd', L'!'};

constexpr bool test_c_array() {
test_string(span_hw, hw);
test_string(span_whw, whw);
test_string(simple_hw, hw);
test_string(simple_hw_s, hw);
test_string(simple_hw_u, hw);
#ifdef __cpp_char8_t
test_string(simple_hw_u8, hw);

test_string(simple_hw_u8, hw_u8);
test_string(simple_hw, hw_u8);
test_string(simple_hw_s, hw_u8);
test_string(simple_hw_u, hw_u8);
#endif // defined(__cpp_char8_t)

test_string(simple_hw_u16, hw_u16);
test_string(simple_hw_u16s, hw_u16);
test_string(simple_hw_u16u, hw_u16);
test_string(simple_whw, hw_u16);

test_string(simple_hw_u32, hw_u32);
test_string(simple_hw_u32s, hw_u32);
test_string(simple_hw_u32u, hw_u32);
test_string(simple_hw_slong, hw_u32);
test_string(simple_hw_ulong, hw_u32);

test_string(simple_whw, whw);
test_string(simple_whw_s, whw);
test_string(simple_whw_u, whw);
test_string(simple_hw_u16, whw);

return true;
}

constexpr bool test_std_array() {
test_string(to_array(simple_hw), hw);
test_string(to_array(simple_hw_s), hw);
test_string(to_array(simple_hw_u), hw);
#ifdef __cpp_char8_t
test_string(to_array(simple_hw_u8), hw);

test_string(to_array(simple_hw_u8), hw_u8);
test_string(to_array(simple_hw), hw_u8);
test_string(to_array(simple_hw_s), hw_u8);
test_string(to_array(simple_hw_u), hw_u8);
#endif // defined(__cpp_char8_t)

test_string(to_array(simple_hw_u16), hw_u16);
test_string(to_array(simple_hw_u16s), hw_u16);
test_string(to_array(simple_hw_u16u), hw_u16);
test_string(to_array(simple_whw), hw_u16);

test_string(to_array(simple_hw_u32), hw_u32);
test_string(to_array(simple_hw_u32s), hw_u32);
test_string(to_array(simple_hw_u32u), hw_u32);
test_string(to_array(simple_hw_slong), hw_u32);
test_string(to_array(simple_hw_ulong), hw_u32);

test_string(to_array(simple_whw), whw);
test_string(to_array(simple_whw_s), whw);
test_string(to_array(simple_whw_u), whw);
test_string(to_array(simple_hw_u16), whw);

return true;
}

constexpr bool test_lvalue_vector() {
{
vector vec(span_hw.data(), span_hw.data() + span_hw.size());
test_string(vec, hw);
}
{
vector vec(span_whw.data(), span_whw.data() + span_whw.size());
test_string(vec, whw);
}
constexpr auto test_lvalue_vector_helper = []<class Span, class CharT>(const Span& sp, const CharT* cstr) {
vector vec(sp.data(), sp.data() + sp.size());
test_string(vec, cstr);
};

test_lvalue_vector_helper(span_hw, hw);
test_lvalue_vector_helper(span_hw_s, hw);
test_lvalue_vector_helper(span_hw_u, hw);
#ifdef __cpp_char8_t
test_lvalue_vector_helper(span_hw_u8, hw);

test_lvalue_vector_helper(span_hw_u8, hw_u8);
test_lvalue_vector_helper(span_hw, hw_u8);
test_lvalue_vector_helper(span_hw_s, hw_u8);
test_lvalue_vector_helper(span_hw_u, hw_u8);
#endif // defined(__cpp_char8_t)

test_lvalue_vector_helper(span_hw_u16, hw_u16);
test_lvalue_vector_helper(span_hw_u16s, hw_u16);
test_lvalue_vector_helper(span_hw_u16u, hw_u16);
test_lvalue_vector_helper(span_whw, hw_u16);

test_lvalue_vector_helper(span_hw_u32, hw_u32);
test_lvalue_vector_helper(span_hw_u32s, hw_u32);
test_lvalue_vector_helper(span_hw_u32u, hw_u32);
test_lvalue_vector_helper(span_hw_slong, hw_u32);
test_lvalue_vector_helper(span_hw_ulong, hw_u32);

test_lvalue_vector_helper(span_whw, whw);
test_lvalue_vector_helper(span_whw_s, whw);
test_lvalue_vector_helper(span_whw_u, whw);
test_lvalue_vector_helper(span_hw_u16, whw);

return true;
}
Expand All @@ -120,6 +329,20 @@ void test_lvalue_forward_list() {
forward_list lst(span_hw.data(), span_hw.data() + span_hw.size());
test_string(lst, hw);
}
#ifdef __cpp_char8_t
{
forward_list lst(span_hw_u8.data(), span_hw_u8.data() + span_hw_u8.size());
test_string(lst, hw_u8);
}
#endif // defined(__cpp_char8_t)
{
forward_list lst(span_hw_u16.data(), span_hw_u16.data() + span_hw_u16.size());
test_string(lst, hw_u16);
}
{
forward_list lst(span_hw_u32.data(), span_hw_u32.data() + span_hw_u32.size());
test_string(lst, hw_u32);
}
{
forward_list lst(span_whw.data(), span_whw.data() + span_whw.size());
test_string(lst, whw);
Expand All @@ -138,11 +361,20 @@ int main() {
test_c_array();
STATIC_ASSERT(test_c_array());

test_std_array();
STATIC_ASSERT(test_std_array());

test_lvalue_vector();
STATIC_ASSERT(test_lvalue_vector());

test_lvalue_forward_list();

test_in<string_instantiator, const char>();
test_in<wstring_instantiator, const wchar_t>();

#ifdef __cpp_char8_t
test_contiguous<u8string_instantiator, const char8_t>();
#endif // defined(__cpp_char8_t)
test_contiguous<u16string_instantiator, const char16_t>();
test_contiguous<u32string_instantiator, const char32_t>();
}