Skip to content

Commit 86fbf09

Browse files
committed
[libc++] Fix throwing away smaller allocations in string::shrink_to_fit
1 parent 6b21cf8 commit 86fbf09

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)