Skip to content

Commit d49f406

Browse files
committed
[libc++] Fix basic_string not allowing max_size() elements to be stored
1 parent 6dd07b1 commit d49f406

File tree

5 files changed

+77
-31
lines changed

5 files changed

+77
-31
lines changed

libcxx/include/string

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,10 +1305,10 @@ public:
13051305
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type max_size() const _NOEXCEPT {
13061306
size_type __m = __alloc_traits::max_size(__alloc_);
13071307
if (__m <= std::numeric_limits<size_type>::max() / 2) {
1308-
return __m - __alignment;
1308+
return __m - __alignment - 1;
13091309
} else {
13101310
bool __uses_lsb = __endian_factor == 2;
1311-
return __uses_lsb ? __m - __alignment : (__m / 2) - __alignment;
1311+
return __uses_lsb ? __m - __alignment - 1 : (__m / 2) - __alignment - 1;
13121312
}
13131313
}
13141314

@@ -2558,7 +2558,7 @@ _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Trait
25582558
__throw_length_error();
25592559
pointer __old_p = __get_pointer();
25602560
size_type __cap =
2561-
__old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1;
2561+
__old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms;
25622562
auto __allocation = std::__allocate_at_least(__alloc_, __cap + 1);
25632563
pointer __p = __allocation.ptr;
25642564
__begin_lifetime(__p, __allocation.count);

libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,44 +23,44 @@ static const std::size_t alignment = 8;
2323
template <class = int>
2424
TEST_CONSTEXPR_CXX20 void full_size() {
2525
std::string str;
26-
assert(str.max_size() == std::numeric_limits<std::size_t>::max() - alignment);
26+
assert(str.max_size() == std::numeric_limits<std::size_t>::max() - alignment - 1);
2727

2828
#ifndef TEST_HAS_NO_CHAR8_T
2929
std::u8string u8str;
30-
assert(u8str.max_size() == std::numeric_limits<std::size_t>::max() - alignment);
30+
assert(u8str.max_size() == std::numeric_limits<std::size_t>::max() - alignment - 1);
3131
#endif
3232

3333
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
3434
std::wstring wstr;
35-
assert(wstr.max_size() == std::numeric_limits<std::size_t>::max() / sizeof(wchar_t) - alignment);
35+
assert(wstr.max_size() == std::numeric_limits<std::size_t>::max() / sizeof(wchar_t) - alignment - 1);
3636
#endif
3737

3838
std::u16string u16str;
3939
std::u32string u32str;
40-
assert(u16str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment);
41-
assert(u32str.max_size() == std::numeric_limits<std::size_t>::max() / 4 - alignment);
40+
assert(u16str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
41+
assert(u32str.max_size() == std::numeric_limits<std::size_t>::max() / 4 - alignment - 1);
4242
}
4343

4444
template <class = int>
4545
TEST_CONSTEXPR_CXX20 void half_size() {
4646
std::string str;
47-
assert(str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment);
47+
assert(str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
4848

4949
#ifndef TEST_HAS_NO_CHAR8_T
5050
std::u8string u8str;
51-
assert(u8str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment);
51+
assert(u8str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
5252
#endif
5353

5454
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
5555
std::wstring wstr;
5656
assert(wstr.max_size() ==
57-
std::numeric_limits<std::size_t>::max() / std::max<size_t>(2ul, sizeof(wchar_t)) - alignment);
57+
std::numeric_limits<std::size_t>::max() / std::max<size_t>(2ul, sizeof(wchar_t)) - alignment - 1);
5858
#endif
5959

6060
std::u16string u16str;
6161
std::u32string u32str;
62-
assert(u16str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment);
63-
assert(u32str.max_size() == std::numeric_limits<std::size_t>::max() / 4 - alignment);
62+
assert(u16str.max_size() == std::numeric_limits<std::size_t>::max() / 2 - alignment - 1);
63+
assert(u32str.max_size() == std::numeric_limits<std::size_t>::max() / 4 - alignment - 1);
6464
}
6565

6666
TEST_CONSTEXPR_CXX20 bool test() {

libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ TEST_CONSTEXPR_CXX20 void test_resize_max_size(const S& s) {
5353
} catch (const std::bad_alloc&) {
5454
return;
5555
}
56-
assert(s.size() == sz);
56+
assert(s2.size() == sz);
5757
}
5858

5959
template <class S>
@@ -91,8 +91,16 @@ TEST_CONSTEXPR_CXX20 bool test() {
9191
test_string<std::string>();
9292
#if TEST_STD_VER >= 11
9393
test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
94+
test_string<std::basic_string<char, std::char_traits<char>, tiny_size_allocator<64, char> > >();
9495
#endif
9596

97+
{ // Test resizing where we can assume that the allocation succeeds
98+
std::basic_string<char, std::char_traits<char>, tiny_size_allocator<32, char> > str;
99+
auto max_size = str.max_size();
100+
str.resize(max_size);
101+
assert(str.size() == max_size);
102+
}
103+
96104
return true;
97105
}
98106

libcxx/test/std/strings/basic.string/string.capacity/resize_size.pass.cpp

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,10 @@
2020

2121
template <class S>
2222
TEST_CONSTEXPR_CXX20 void test(S s, typename S::size_type n, S expected) {
23-
if (n <= s.max_size()) {
24-
s.resize(n);
25-
LIBCPP_ASSERT(s.__invariants());
26-
assert(s == expected);
27-
LIBCPP_ASSERT(is_string_asan_correct(s));
28-
}
29-
#ifndef TEST_HAS_NO_EXCEPTIONS
30-
else if (!TEST_IS_CONSTANT_EVALUATED) {
31-
try {
32-
s.resize(n);
33-
assert(false);
34-
} catch (std::length_error&) {
35-
assert(n > s.max_size());
36-
}
37-
}
38-
#endif
23+
s.resize(n);
24+
LIBCPP_ASSERT(s.__invariants());
25+
assert(s == expected);
26+
LIBCPP_ASSERT(is_string_asan_correct(s));
3927
}
4028

4129
template <class S>
@@ -56,7 +44,26 @@ TEST_CONSTEXPR_CXX20 void test_string() {
5644
test(S("12345678901234567890123456789012345678901234567890"),
5745
60,
5846
S("12345678901234567890123456789012345678901234567890\0\0\0\0\0\0\0\0\0\0", 60));
59-
test(S(), S::npos, S("not going to happen"));
47+
}
48+
49+
template <class CharT>
50+
TEST_CONSTEXPR_CXX20 void test_max_size() {
51+
#ifndef TEST_HAS_NO_EXCEPTIONS
52+
if (!TEST_IS_CONSTANT_EVALUATED) {
53+
std::basic_string<CharT> str;
54+
try {
55+
str.resize(std::string::npos);
56+
assert(false);
57+
} catch (const std::length_error&) {
58+
}
59+
}
60+
#endif
61+
62+
{
63+
std::basic_string<CharT, std::char_traits<CharT>, tiny_size_allocator<32, CharT>> str;
64+
str.resize(str.max_size());
65+
assert(str.size() == str.max_size());
66+
}
6067
}
6168

6269
TEST_CONSTEXPR_CXX20 bool test() {
@@ -66,6 +73,9 @@ TEST_CONSTEXPR_CXX20 bool test() {
6673
test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
6774
#endif
6875

76+
test_max_size<char>();
77+
test_max_size<wchar_t>();
78+
6979
return true;
7080
}
7181

libcxx/test/support/min_allocator.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,4 +480,32 @@ class safe_allocator {
480480
TEST_CONSTEXPR_CXX20 friend bool operator!=(safe_allocator x, safe_allocator y) { return !(x == y); }
481481
};
482482

483+
template <std::size_t MaxSize, class T>
484+
struct tiny_size_allocator {
485+
using value_type = T;
486+
using size_type = unsigned;
487+
488+
template <class U>
489+
struct rebind {
490+
using other = tiny_size_allocator<MaxSize, T>;
491+
};
492+
493+
tiny_size_allocator() = default;
494+
495+
template <class U>
496+
TEST_CONSTEXPR_CXX20 tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
497+
498+
TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
499+
assert(n <= MaxSize);
500+
return std::allocator<T>{}.allocate(n);
501+
}
502+
503+
TEST_CONSTEXPR_CXX20 void deallocate(T* ptr, std::size_t n) { std::allocator<T>{}.deallocate(ptr, n); }
504+
505+
TEST_CONSTEXPR_CXX20 size_type max_size() const { return MaxSize; }
506+
507+
friend bool operator==(tiny_size_allocator, tiny_size_allocator) { return true; }
508+
friend bool operator!=(tiny_size_allocator, tiny_size_allocator) { return false; }
509+
};
510+
483511
#endif // MIN_ALLOCATOR_H

0 commit comments

Comments
 (0)