Skip to content

Commit f65d7fd

Browse files
mordanteMitalAshok
andauthored
[libc++][vector] Fixes shrink_to_fit. (llvm#97895)
This assures shrink_to_fit does not increase the allocated size. Partly addresses llvm#95161 --------- Co-authored-by: Mital Ashok <[email protected]>
1 parent b4f3a96 commit f65d7fd

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

libcxx/include/vector

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::shrink_to_fit() _NOE
14431443
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
14441444
allocator_type& __a = this->__alloc();
14451445
__split_buffer<value_type, allocator_type&> __v(size(), size(), __a);
1446-
__swap_out_circular_buffer(__v);
1446+
// The Standard mandates shrink_to_fit() does not increase the capacity.
1447+
// With equal capacity keep the existing buffer. This avoids extra work
1448+
// due to swapping the elements.
1449+
if (__v.capacity() < capacity())
1450+
__swap_out_circular_buffer(__v);
14471451
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
14481452
} catch (...) {
14491453
}

libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit.pass.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,56 @@ TEST_CONSTEXPR_CXX20 bool tests() {
7171
return true;
7272
}
7373

74+
#if TEST_STD_VER >= 23
75+
template <typename T>
76+
struct increasing_allocator {
77+
using value_type = T;
78+
std::size_t min_elements = 1000;
79+
increasing_allocator() = default;
80+
81+
template <typename U>
82+
constexpr increasing_allocator(const increasing_allocator<U>& other) noexcept : min_elements(other.min_elements) {}
83+
84+
constexpr std::allocation_result<T*> allocate_at_least(std::size_t n) {
85+
if (n < min_elements)
86+
n = min_elements;
87+
min_elements += 1000;
88+
return std::allocator<T>{}.allocate_at_least(n);
89+
}
90+
constexpr T* allocate(std::size_t n) { return allocate_at_least(n).ptr; }
91+
constexpr void deallocate(T* p, std::size_t n) noexcept { std::allocator<T>{}.deallocate(p, n); }
92+
};
93+
94+
template <typename T, typename U>
95+
bool operator==(increasing_allocator<T>, increasing_allocator<U>) {
96+
return true;
97+
}
98+
99+
// https://github.com/llvm/llvm-project/issues/95161
100+
constexpr bool test_increasing_allocator() {
101+
std::vector<int, increasing_allocator<int>> v;
102+
v.push_back(1);
103+
assert(is_contiguous_container_asan_correct(v));
104+
std::size_t capacity = v.capacity();
105+
v.shrink_to_fit();
106+
assert(v.capacity() <= capacity);
107+
assert(v.size() == 1);
108+
assert(is_contiguous_container_asan_correct(v));
109+
110+
return true;
111+
}
112+
#endif // TEST_STD_VER >= 23
113+
74114
int main(int, char**)
75115
{
76-
tests();
116+
tests();
77117
#if TEST_STD_VER > 17
78118
static_assert(tests());
79119
#endif
120+
#if TEST_STD_VER >= 23
121+
test_increasing_allocator();
122+
static_assert(test_increasing_allocator());
123+
#endif
124+
80125
return 0;
81126
}

0 commit comments

Comments
 (0)