xref: /llvm-project/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp (revision d0ca9f23e8f25b0509c3ff34ed215508b39ea6e7)
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 // <string>
10 
11 // void shrink_to_fit(); // constexpr since C++20
12 
13 #include <string>
14 #include <cassert>
15 
16 #include "test_macros.h"
17 #include "min_allocator.h"
18 #include "asan_testing.h"
19 
20 template <class S>
21 TEST_CONSTEXPR_CXX20 void test(S s) {
22   typename S::size_type old_cap = s.capacity();
23   S s0                          = s;
24   s.shrink_to_fit();
25   LIBCPP_ASSERT(s.__invariants());
26   assert(s == s0);
27   assert(s.capacity() <= old_cap);
28   assert(s.capacity() >= s.size());
29   LIBCPP_ASSERT(is_string_asan_correct(s));
30 }
31 
32 template <class S>
33 TEST_CONSTEXPR_CXX20 void test_string() {
34   S s;
35   test(s);
36 
37   s.assign(10, 'a');
38   s.erase(5);
39   test(s);
40 
41   s.assign(50, 'a');
42   s.erase(5);
43   test(s);
44 
45   s.assign(100, 'a');
46   s.erase(50);
47   test(s);
48 
49   s.assign(100, 'a');
50   for (int i = 0; i <= 9; ++i) {
51     s.erase(90 - 10 * i);
52     test(s);
53   }
54 }
55 
56 TEST_CONSTEXPR_CXX20 bool test() {
57   test_string<std::string>();
58 #if TEST_STD_VER >= 11
59   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
60   test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
61 #endif
62 
63   return true;
64 }
65 
66 #if TEST_STD_VER >= 23
67 std::size_t min_bytes = 1000;
68 
69 template <typename T>
70 struct increasing_allocator {
71   using value_type       = T;
72   increasing_allocator() = default;
73   template <typename U>
74   increasing_allocator(const increasing_allocator<U>&) noexcept {}
75   std::allocation_result<T*> allocate_at_least(std::size_t n) {
76     std::size_t allocation_amount = n * sizeof(T);
77     if (allocation_amount < min_bytes)
78       allocation_amount = min_bytes;
79     min_bytes += 1000;
80     return {static_cast<T*>(::operator new(allocation_amount)), allocation_amount / sizeof(T)};
81   }
82   T* allocate(std::size_t n) { return allocate_at_least(n).ptr; }
83   void deallocate(T* p, std::size_t) noexcept { ::operator delete(static_cast<void*>(p)); }
84 };
85 
86 template <typename T, typename U>
87 bool operator==(increasing_allocator<T>, increasing_allocator<U>) {
88   return true;
89 }
90 
91 // https://github.com/llvm/llvm-project/issues/95161
92 void test_increasing_allocator() {
93   std::basic_string<char, std::char_traits<char>, increasing_allocator<char>> s{
94       "String does not fit in the internal buffer"};
95   std::size_t capacity = s.capacity();
96   std::size_t size     = s.size();
97   s.shrink_to_fit();
98   assert(s.capacity() <= capacity);
99   assert(s.size() == size);
100   LIBCPP_ASSERT(is_string_asan_correct(s));
101 }
102 #endif // TEST_STD_VER >= 23
103 
104 int main(int, char**) {
105   test();
106 #if TEST_STD_VER >= 23
107   test_increasing_allocator();
108 #endif
109 #if TEST_STD_VER > 17
110   static_assert(test());
111 #endif
112 
113   return 0;
114 }
115