Skip to content

Commit 4a68e4c

Browse files
authored
[libc++] Fix throwing away smaller allocations in string::shrink_to_fit (#115659)
Currently `string::shrink_to_fit()` throws away any allocations which return more capacity than we requested, even if that allocation is still smaller than the current capacity. This patch fixes this to compare the returned allocation against the current capacity of the string instead of against the requested capacity.
1 parent bf601ba commit 4a68e4c

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

libcxx/include/string

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3393,7 +3393,7 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
33933393
// The Standard mandates shrink_to_fit() does not increase the capacity.
33943394
// With equal capacity keep the existing buffer. This avoids extra work
33953395
// due to swapping the elements.
3396-
if (__allocation.count - 1 > __target_capacity) {
3396+
if (__allocation.count - 1 > capacity()) {
33973397
__alloc_traits::deallocate(__alloc_, __allocation.ptr, __allocation.count);
33983398
__annotate_new(__sz); // Undoes the __annotate_delete()
33993399
return;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
// <string>
12+
13+
// void shrink_to_fit(); // constexpr since C++20
14+
15+
// Make sure we use an allocation returned by allocate_at_least if it is smaller than the current allocation
16+
// even if it contains more bytes than we requested
17+
18+
#include <cassert>
19+
#include <string>
20+
21+
template <typename T>
22+
struct oversizing_allocator {
23+
using value_type = T;
24+
oversizing_allocator() = default;
25+
template <typename U>
26+
oversizing_allocator(const oversizing_allocator<U>&) noexcept {}
27+
std::allocation_result<T*> allocate_at_least(std::size_t n) {
28+
++n;
29+
return {static_cast<T*>(::operator new(n * sizeof(T))), n};
30+
}
31+
T* allocate(std::size_t n) { return allocate_at_least(n).ptr; }
32+
void deallocate(T* p, std::size_t) noexcept { ::operator delete(static_cast<void*>(p)); }
33+
};
34+
35+
template <typename T, typename U>
36+
bool operator==(oversizing_allocator<T>, oversizing_allocator<U>) {
37+
return true;
38+
}
39+
40+
void test_oversizing_allocator() {
41+
std::basic_string<char, std::char_traits<char>, oversizing_allocator<char>> s{
42+
"String does not fit in the internal buffer and is a bit longer"};
43+
s = "String does not fit in the internal buffer";
44+
std::size_t capacity = s.capacity();
45+
std::size_t size = s.size();
46+
s.shrink_to_fit();
47+
assert(s.capacity() < capacity);
48+
assert(s.size() == size);
49+
}
50+
51+
int main(int, char**) {
52+
test_oversizing_allocator();
53+
54+
return 0;
55+
}

0 commit comments

Comments
 (0)