|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef TEST_SUPPORT_INCREASING_ALLOCATOR_H | 
|  | #define TEST_SUPPORT_INCREASING_ALLOCATOR_H | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <limits> | 
|  | #include <memory> | 
|  |  | 
|  | #include "test_macros.h" | 
|  |  | 
|  | // The increasing_allocator is a custom allocator that enforces an increasing minimum allocation size, | 
|  | // ensuring that it allocates an increasing amount of memory, possibly exceeding the requested amount. | 
|  | // This unique behavior is particularly useful for testing the shrink_to_fit functionality in std::vector, | 
|  | // vector<bool>, and std::basic_string, ensuring that shrink_to_fit does not increase the capacity of | 
|  | // the allocated memory. | 
|  |  | 
|  | template <typename T> | 
|  | struct increasing_allocator { | 
|  | using value_type         = T; | 
|  | std::size_t min_elements = 1000; | 
|  | increasing_allocator()   = default; | 
|  |  | 
|  | template <typename U> | 
|  | TEST_CONSTEXPR_CXX20 increasing_allocator(const increasing_allocator<U>& other) TEST_NOEXCEPT | 
|  | : min_elements(other.min_elements) {} | 
|  |  | 
|  | #if TEST_STD_VER >= 23 | 
|  | TEST_CONSTEXPR_CXX23 std::allocation_result<T*> allocate_at_least(std::size_t n) { | 
|  | if (n < min_elements) | 
|  | n = min_elements; | 
|  | min_elements += 1000; | 
|  | return std::allocator<T>{}.allocate_at_least(n); | 
|  | } | 
|  | #endif // TEST_STD_VER >= 23 | 
|  |  | 
|  | TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); } | 
|  |  | 
|  | TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) TEST_NOEXCEPT { std::allocator<T>().deallocate(p, n); } | 
|  | }; | 
|  |  | 
|  | template <typename T, typename U> | 
|  | TEST_CONSTEXPR_CXX20 bool operator==(increasing_allocator<T>, increasing_allocator<U>) TEST_NOEXCEPT { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <std::size_t MinAllocSize, typename T> | 
|  | class min_size_allocator { | 
|  | public: | 
|  | using value_type     = T; | 
|  | min_size_allocator() = default; | 
|  |  | 
|  | template <typename U> | 
|  | TEST_CONSTEXPR_CXX20 min_size_allocator(const min_size_allocator<MinAllocSize, U>&) TEST_NOEXCEPT {} | 
|  |  | 
|  | TEST_NODISCARD TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { | 
|  | if (n < MinAllocSize) | 
|  | n = MinAllocSize; | 
|  | return static_cast<T*>(::operator new(n * sizeof(T))); | 
|  | } | 
|  |  | 
|  | TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t) TEST_NOEXCEPT { ::operator delete(static_cast<void*>(p)); } | 
|  |  | 
|  | template <typename U> | 
|  | struct rebind { | 
|  | using other = min_size_allocator<MinAllocSize, U>; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | template <std::size_t MinAllocSize, typename T, typename U> | 
|  | TEST_CONSTEXPR_CXX20 bool | 
|  | operator==(const min_size_allocator<MinAllocSize, T>&, const min_size_allocator<MinAllocSize, U>&) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <std::size_t MinAllocSize, typename T, typename U> | 
|  | TEST_CONSTEXPR_CXX20 bool | 
|  | operator!=(const min_size_allocator<MinAllocSize, T>&, const min_size_allocator<MinAllocSize, U>&) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | class pow2_allocator { | 
|  | public: | 
|  | using value_type = T; | 
|  | pow2_allocator() = default; | 
|  |  | 
|  | template <typename U> | 
|  | TEST_CONSTEXPR_CXX20 pow2_allocator(const pow2_allocator<U>&) TEST_NOEXCEPT {} | 
|  |  | 
|  | TEST_NODISCARD TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { | 
|  | return static_cast<T*>(::operator new(next_power_of_two(n) * sizeof(T))); | 
|  | } | 
|  |  | 
|  | TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t) TEST_NOEXCEPT { ::operator delete(static_cast<void*>(p)); } | 
|  |  | 
|  | private: | 
|  | TEST_CONSTEXPR_CXX20 std::size_t next_power_of_two(std::size_t n) const { | 
|  | if ((n & (n - 1)) == 0) | 
|  | return n; | 
|  | for (std::size_t shift = 1; shift < std::numeric_limits<std::size_t>::digits; shift <<= 1) { | 
|  | n |= n >> shift; | 
|  | } | 
|  | return n + 1; | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename T, typename U> | 
|  | TEST_CONSTEXPR_CXX20 bool operator==(const pow2_allocator<T>&, const pow2_allocator<U>&) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template <typename T, typename U> | 
|  | TEST_CONSTEXPR_CXX20 bool operator!=(const pow2_allocator<T>&, const pow2_allocator<U>&) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #endif // TEST_SUPPORT_INCREASING_ALLOCATOR_H |