1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef TEST_ALLOCATOR_H
10 #define TEST_ALLOCATOR_H
12 #include <type_traits>
21 #include "test_macros.h"
23 template <class Alloc
>
24 TEST_CONSTEXPR_CXX20
inline typename
std::allocator_traits
<Alloc
>::size_type
alloc_max_size(Alloc
const& a
) {
25 typedef std::allocator_traits
<Alloc
> AT
;
26 return AT::max_size(a
);
29 struct test_allocator_statistics
{
30 int time_to_throw
= 0;
31 int throw_after
= INT_MAX
;
32 int count
= 0; // the number of active instances
33 int alloc_count
= 0; // the number of allocations not deallocating
34 int allocated_size
= 0; // the size of allocated elements
35 int construct_count
= 0; // the number of times that ::construct was called
36 int destroy_count
= 0; // the number of times that ::destroy was called
41 TEST_CONSTEXPR_CXX14
void clear() {
42 assert(count
== 0 && "clearing leaking allocator data?");
49 throw_after
= INT_MAX
;
50 clear_ctor_counters();
53 TEST_CONSTEXPR_CXX14
void clear_ctor_counters() {
60 struct test_alloc_base
{
61 TEST_CONSTEXPR
static const int destructed_value
= -1;
62 TEST_CONSTEXPR
static const int moved_value
= INT_MAX
;
66 class test_allocator
{
67 int data_
= 0; // participates in equality
68 int id_
= 0; // unique identifier, doesn't participate in equality
69 test_allocator_statistics
* stats_
= nullptr;
72 friend class test_allocator
;
75 typedef unsigned size_type
;
76 typedef int difference_type
;
78 typedef value_type
* pointer
;
79 typedef const value_type
* const_pointer
;
80 typedef typename
std::add_lvalue_reference
<value_type
>::type reference
;
81 typedef typename
std::add_lvalue_reference
<const value_type
>::type const_reference
;
85 typedef test_allocator
<U
> other
;
88 TEST_CONSTEXPR
test_allocator() TEST_NOEXCEPT
= default;
90 TEST_CONSTEXPR_CXX14
explicit test_allocator(test_allocator_statistics
* stats
) TEST_NOEXCEPT
: stats_(stats
) {
91 if (stats_
!= nullptr)
95 TEST_CONSTEXPR
explicit test_allocator(int data
) TEST_NOEXCEPT
: data_(data
) {}
97 TEST_CONSTEXPR_CXX14
explicit test_allocator(int data
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
98 : data_(data
), stats_(stats
) {
103 TEST_CONSTEXPR
explicit test_allocator(int data
, int id
) TEST_NOEXCEPT
: data_(data
), id_(id
) {}
105 TEST_CONSTEXPR_CXX14
explicit test_allocator(int data
, int id
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
106 : data_(data
), id_(id
), stats_(stats
) {
107 if (stats_
!= nullptr)
111 TEST_CONSTEXPR_CXX14
test_allocator(const test_allocator
& a
) TEST_NOEXCEPT
112 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
) {
113 assert(a
.data_
!= test_alloc_base::destructed_value
&& a
.id_
!= test_alloc_base::destructed_value
&&
114 "copying from destroyed allocator");
115 if (stats_
!= nullptr) {
121 TEST_CONSTEXPR_CXX14
test_allocator(test_allocator
&& a
) TEST_NOEXCEPT
: data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
) {
122 if (stats_
!= nullptr) {
126 assert(a
.data_
!= test_alloc_base::destructed_value
&& a
.id_
!= test_alloc_base::destructed_value
&&
127 "moving from destroyed allocator");
128 a
.id_
= test_alloc_base::moved_value
;
132 TEST_CONSTEXPR_CXX14
test_allocator(const test_allocator
<U
>& a
) TEST_NOEXCEPT
133 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
) {
134 if (stats_
!= nullptr) {
140 TEST_CONSTEXPR_CXX20
~test_allocator() TEST_NOEXCEPT
{
141 assert(data_
!= test_alloc_base::destructed_value
);
142 assert(id_
!= test_alloc_base::destructed_value
);
143 if (stats_
!= nullptr)
145 data_
= test_alloc_base::destructed_value
;
146 id_
= test_alloc_base::destructed_value
;
149 TEST_CONSTEXPR pointer
address(reference x
) const { return &x
; }
150 TEST_CONSTEXPR const_pointer
address(const_reference x
) const { return &x
; }
152 TEST_CONSTEXPR_CXX14 pointer
allocate(size_type n
, const void* = nullptr) {
153 assert(data_
!= test_alloc_base::destructed_value
);
154 if (stats_
!= nullptr) {
155 if (stats_
->time_to_throw
>= stats_
->throw_after
)
156 TEST_THROW(std::bad_alloc());
157 ++stats_
->time_to_throw
;
158 ++stats_
->alloc_count
;
159 stats_
->allocated_size
+= n
;
161 return std::allocator
<value_type
>().allocate(n
);
164 TEST_CONSTEXPR_CXX14
void deallocate(pointer p
, size_type s
) {
165 assert(data_
!= test_alloc_base::destructed_value
);
166 if (stats_
!= nullptr) {
167 --stats_
->alloc_count
;
168 stats_
->allocated_size
-= s
;
170 std::allocator
<value_type
>().deallocate(p
, s
);
173 TEST_CONSTEXPR size_type
max_size() const TEST_NOEXCEPT
{ return UINT_MAX
/ sizeof(T
); }
176 TEST_CONSTEXPR_CXX20
void construct(pointer p
, U
&& val
) {
177 if (stats_
!= nullptr)
178 ++stats_
->construct_count
;
179 #if TEST_STD_VER > 17
180 std::construct_at(std::to_address(p
), std::forward
<U
>(val
));
182 ::new (static_cast<void*>(p
)) T(std::forward
<U
>(val
));
186 TEST_CONSTEXPR_CXX14
void destroy(pointer p
) {
187 if (stats_
!= nullptr)
188 ++stats_
->destroy_count
;
191 TEST_CONSTEXPR
friend bool operator==(const test_allocator
& x
, const test_allocator
& y
) { return x
.data_
== y
.data_
; }
192 TEST_CONSTEXPR
friend bool operator!=(const test_allocator
& x
, const test_allocator
& y
) { return !(x
== y
); }
194 TEST_CONSTEXPR
int get_data() const { return data_
; }
195 TEST_CONSTEXPR
int get_id() const { return id_
; }
199 class test_allocator
<void> {
202 test_allocator_statistics
* stats_
= nullptr;
205 friend class test_allocator
;
208 typedef unsigned size_type
;
209 typedef int difference_type
;
210 typedef void value_type
;
211 typedef value_type
* pointer
;
212 typedef const value_type
* const_pointer
;
216 typedef test_allocator
<U
> other
;
219 TEST_CONSTEXPR
test_allocator() TEST_NOEXCEPT
= default;
221 TEST_CONSTEXPR_CXX14
explicit test_allocator(test_allocator_statistics
* stats
) TEST_NOEXCEPT
: stats_(stats
) {}
223 TEST_CONSTEXPR
explicit test_allocator(int data
) TEST_NOEXCEPT
: data_(data
) {}
225 TEST_CONSTEXPR
explicit test_allocator(int data
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
226 : data_(data
), stats_(stats
)
229 TEST_CONSTEXPR
explicit test_allocator(int data
, int id
) : data_(data
), id_(id
) {}
231 TEST_CONSTEXPR_CXX14
explicit test_allocator(int data
, int id
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
232 : data_(data
), id_(id
), stats_(stats
)
235 TEST_CONSTEXPR_CXX14
explicit test_allocator(const test_allocator
& a
) TEST_NOEXCEPT
236 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
)
240 TEST_CONSTEXPR_CXX14
test_allocator(const test_allocator
<U
>& a
) TEST_NOEXCEPT
241 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
)
244 TEST_CONSTEXPR_CXX20
~test_allocator() TEST_NOEXCEPT
{
245 data_
= test_alloc_base::destructed_value
;
246 id_
= test_alloc_base::destructed_value
;
249 TEST_CONSTEXPR
int get_id() const { return id_
; }
250 TEST_CONSTEXPR
int get_data() const { return data_
; }
252 TEST_CONSTEXPR
friend bool operator==(const test_allocator
& x
, const test_allocator
& y
) { return x
.data_
== y
.data_
; }
253 TEST_CONSTEXPR
friend bool operator!=(const test_allocator
& x
, const test_allocator
& y
) { return !(x
== y
); }
257 class other_allocator
{
261 friend class other_allocator
;
264 typedef T value_type
;
266 TEST_CONSTEXPR_CXX14
other_allocator() {}
267 TEST_CONSTEXPR_CXX14
explicit other_allocator(int i
) : data_(i
) {}
270 TEST_CONSTEXPR_CXX14
other_allocator(const other_allocator
<U
>& a
) : data_(a
.data_
) {}
272 TEST_CONSTEXPR_CXX20 T
* allocate(std::size_t n
) { return std::allocator
<value_type
>().allocate(n
); }
273 TEST_CONSTEXPR_CXX20
void deallocate(T
* p
, std::size_t s
) { std::allocator
<value_type
>().deallocate(p
, s
); }
275 TEST_CONSTEXPR_CXX14 other_allocator
select_on_container_copy_construction() const { return other_allocator(-2); }
277 TEST_CONSTEXPR_CXX14
friend bool operator==(const other_allocator
& x
, const other_allocator
& y
) {
278 return x
.data_
== y
.data_
;
281 TEST_CONSTEXPR_CXX14
friend bool operator!=(const other_allocator
& x
, const other_allocator
& y
) { return !(x
== y
); }
282 TEST_CONSTEXPR
int get_data() const { return data_
; }
284 typedef std::true_type propagate_on_container_copy_assignment
;
285 typedef std::true_type propagate_on_container_move_assignment
;
286 typedef std::true_type propagate_on_container_swap
;
288 #if TEST_STD_VER < 11
289 std::size_t max_size() const { return UINT_MAX
/ sizeof(T
); }
295 template <typename T
>
296 class TaggingAllocator
;
299 // All constructors must be passed the Tag type.
301 // DefaultInsertable into vector<X, TaggingAllocator<X>>,
302 TEST_CONSTEXPR
Tag_X(Ctor_Tag
) {}
303 // CopyInsertable into vector<X, TaggingAllocator<X>>,
304 TEST_CONSTEXPR
Tag_X(Ctor_Tag
, const Tag_X
&) {}
305 // MoveInsertable into vector<X, TaggingAllocator<X>>, and
306 TEST_CONSTEXPR
Tag_X(Ctor_Tag
, Tag_X
&&) {}
308 // EmplaceConstructible into vector<X, TaggingAllocator<X>> from args.
309 template <typename
... Args
>
310 TEST_CONSTEXPR
Tag_X(Ctor_Tag
, Args
&&...) {}
312 // not DefaultConstructible, CopyConstructible or MoveConstructible.
314 Tag_X(const Tag_X
&) = delete;
315 Tag_X(Tag_X
&&) = delete;
318 TEST_CONSTEXPR_CXX14 Tag_X
& operator=(const Tag_X
&) { return *this; };
321 TEST_CONSTEXPR_CXX14 Tag_X
& operator=(Tag_X
&&) { return *this; };
325 // Erasable from vector<X, TaggingAllocator<X>>.
326 friend class TaggingAllocator
<Tag_X
>;
329 template <typename T
>
330 class TaggingAllocator
{
332 using value_type
= T
;
333 TaggingAllocator() = default;
335 template <typename U
>
336 TEST_CONSTEXPR
TaggingAllocator(const TaggingAllocator
<U
>&) {}
338 template <typename
... Args
>
339 TEST_CONSTEXPR_CXX20
void construct(Tag_X
* p
, Args
&&... args
) {
340 #if TEST_STD_VER > 17
341 std::construct_at(p
, Ctor_Tag
{}, std::forward
<Args
>(args
)...);
343 ::new (static_cast<void*>(p
)) Tag_X(Ctor_Tag(), std::forward
<Args
>(args
)...);
347 template <typename U
>
348 TEST_CONSTEXPR_CXX20
void destroy(U
* p
) {
352 TEST_CONSTEXPR_CXX20 T
* allocate(std::size_t n
) { return std::allocator
<T
>().allocate(n
); }
353 TEST_CONSTEXPR_CXX20
void deallocate(T
* p
, std::size_t n
) { std::allocator
<T
>().deallocate(p
, n
); }
356 template <std::size_t MaxAllocs
>
357 struct limited_alloc_handle
{
358 std::size_t outstanding_
= 0;
359 void* last_alloc_
= nullptr;
362 TEST_CONSTEXPR_CXX20 T
* allocate(std::size_t N
) {
363 if (N
+ outstanding_
> MaxAllocs
)
364 TEST_THROW(std::bad_alloc());
365 auto alloc
= std::allocator
<T
>().allocate(N
);
372 TEST_CONSTEXPR_CXX20
void deallocate(T
* ptr
, std::size_t N
) {
373 if (ptr
== last_alloc_
) {
374 last_alloc_
= nullptr;
375 assert(outstanding_
>= N
);
378 std::allocator
<T
>().deallocate(ptr
, N
);
384 class thread_unsafe_shared_ptr
{
386 thread_unsafe_shared_ptr() = default;
388 TEST_CONSTEXPR_CXX14
thread_unsafe_shared_ptr(const thread_unsafe_shared_ptr
& other
) : block(other
.block
) {
392 TEST_CONSTEXPR_CXX20
~thread_unsafe_shared_ptr() {
394 if (block
->ref_count
!= 0)
396 typedef std::allocator_traits
<std::allocator
<control_block
> > allocator_traits
;
397 std::allocator
<control_block
> alloc
;
398 allocator_traits::destroy(alloc
, block
);
399 allocator_traits::deallocate(alloc
, block
, 1);
402 TEST_CONSTEXPR
const T
& operator*() const { return block
->content
; }
403 TEST_CONSTEXPR
const T
* operator->() const { return &block
->content
; }
404 TEST_CONSTEXPR_CXX14 T
& operator*() { return block
->content
; }
405 TEST_CONSTEXPR_CXX14 T
* operator->() { return &block
->content
; }
406 TEST_CONSTEXPR_CXX14 T
* get() { return &block
->content
; }
407 TEST_CONSTEXPR
const T
* get() const { return &block
->content
; }
410 struct control_block
{
411 template <class... Args
>
412 TEST_CONSTEXPR
control_block(Args
... args
) : content(std::forward
<Args
>(args
)...) {}
413 std::size_t ref_count
= 1;
417 control_block
* block
= nullptr;
419 template <class U
, class... Args
>
420 friend TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr
<U
> make_thread_unsafe_shared(Args
...);
423 template <class T
, class... Args
>
424 TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr
<T
> make_thread_unsafe_shared(Args
... args
) {
425 typedef typename thread_unsafe_shared_ptr
<T
>::control_block control_block_type
;
426 typedef std::allocator_traits
<std::allocator
<control_block_type
> > allocator_traits
;
428 thread_unsafe_shared_ptr
<T
> ptr
;
429 std::allocator
<control_block_type
> alloc
;
430 ptr
.block
= allocator_traits::allocate(alloc
, 1);
431 allocator_traits::construct(alloc
, ptr
.block
, std::forward
<Args
>(args
)...);
435 } // namespace detail
437 template <class T
, std::size_t N
>
438 class limited_allocator
{
439 template <class U
, std::size_t UN
>
440 friend class limited_allocator
;
441 typedef limited_alloc_handle
<N
> BuffT
;
442 detail::thread_unsafe_shared_ptr
<BuffT
> handle_
;
445 typedef T value_type
;
446 typedef value_type
* pointer
;
447 typedef const value_type
* const_pointer
;
448 typedef value_type
& reference
;
449 typedef const value_type
& const_reference
;
450 typedef std::size_t size_type
;
451 typedef std::ptrdiff_t difference_type
;
455 typedef limited_allocator
<U
, N
> other
;
458 TEST_CONSTEXPR_CXX20
limited_allocator() : handle_(detail::make_thread_unsafe_shared
<BuffT
>()) {}
460 limited_allocator(limited_allocator
const&) = default;
463 TEST_CONSTEXPR
explicit limited_allocator(limited_allocator
<U
, N
> const& other
) : handle_(other
.handle_
) {}
465 limited_allocator
& operator=(const limited_allocator
&) = delete;
467 TEST_CONSTEXPR_CXX20 pointer
allocate(size_type n
) { return handle_
->template allocate
<T
>(n
); }
468 TEST_CONSTEXPR_CXX20
void deallocate(pointer p
, size_type n
) { handle_
->template deallocate
<T
>(p
, n
); }
469 TEST_CONSTEXPR size_type
max_size() const { return N
; }
470 TEST_CONSTEXPR BuffT
* getHandle() const { return handle_
.get(); }
473 template <class T
, class U
, std::size_t N
>
474 TEST_CONSTEXPR
inline bool operator==(limited_allocator
<T
, N
> const& LHS
, limited_allocator
<U
, N
> const& RHS
) {
475 return LHS
.getHandle() == RHS
.getHandle();
478 template <class T
, class U
, std::size_t N
>
479 TEST_CONSTEXPR
inline bool operator!=(limited_allocator
<T
, N
> const& LHS
, limited_allocator
<U
, N
> const& RHS
) {
480 return !(LHS
== RHS
);
483 // Track the "provenance" of this allocator instance: how many times was
484 // select_on_container_copy_construction called in order to produce it?
487 struct SocccAllocator
{
488 using value_type
= T
;
491 explicit SocccAllocator(int i
) : count_(i
) {}
494 SocccAllocator(const SocccAllocator
<U
>& a
) : count_(a
.count_
) {}
496 T
* allocate(std::size_t n
) { return std::allocator
<T
>().allocate(n
); }
497 void deallocate(T
* p
, std::size_t n
) { std::allocator
<T
>().deallocate(p
, n
); }
499 SocccAllocator
select_on_container_copy_construction() const { return SocccAllocator(count_
+ 1); }
501 bool operator==(const SocccAllocator
&) const { return true; }
503 using propagate_on_container_copy_assignment
= std::false_type
;
504 using propagate_on_container_move_assignment
= std::false_type
;
505 using propagate_on_container_swap
= std::false_type
;
508 #endif // TEST_ALLOCATOR_H