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 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
;
38 TEST_CONSTEXPR_CXX14
void clear() {
39 assert(count
== 0 && "clearing leaking allocator data?");
43 throw_after
= INT_MAX
;
44 clear_ctor_counters();
47 TEST_CONSTEXPR_CXX14
void clear_ctor_counters() {
54 struct test_alloc_base
{
55 TEST_CONSTEXPR
static const int destructed_value
= -1;
56 TEST_CONSTEXPR
static const int moved_value
= INT_MAX
;
60 class test_allocator
{
61 int data_
= 0; // participates in equality
62 int id_
= 0; // unique identifier, doesn't participate in equality
63 test_allocator_statistics
* stats_
= nullptr;
66 friend class test_allocator
;
69 typedef unsigned size_type
;
70 typedef int difference_type
;
72 typedef value_type
* pointer
;
73 typedef const value_type
* const_pointer
;
74 typedef typename
std::add_lvalue_reference
<value_type
>::type reference
;
75 typedef typename
std::add_lvalue_reference
<const value_type
>::type const_reference
;
79 typedef test_allocator
<U
> other
;
82 TEST_CONSTEXPR
test_allocator() TEST_NOEXCEPT
= default;
84 TEST_CONSTEXPR_CXX14
explicit test_allocator(test_allocator_statistics
* stats
) TEST_NOEXCEPT
: stats_(stats
) {
85 if (stats_
!= nullptr)
89 TEST_CONSTEXPR
explicit test_allocator(int data
) TEST_NOEXCEPT
: data_(data
) {}
91 TEST_CONSTEXPR_CXX14
explicit test_allocator(int data
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
92 : data_(data
), stats_(stats
) {
97 TEST_CONSTEXPR
explicit test_allocator(int data
, int id
) TEST_NOEXCEPT
: data_(data
), id_(id
) {}
99 TEST_CONSTEXPR_CXX14
explicit test_allocator(int data
, int id
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
100 : data_(data
), id_(id
), stats_(stats
) {
101 if (stats_
!= nullptr)
105 TEST_CONSTEXPR_CXX14
test_allocator(const test_allocator
& a
) TEST_NOEXCEPT
106 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
) {
107 assert(a
.data_
!= test_alloc_base::destructed_value
&& a
.id_
!= test_alloc_base::destructed_value
&&
108 "copying from destroyed allocator");
109 if (stats_
!= nullptr) {
115 #if TEST_STD_VER >= 11
116 TEST_CONSTEXPR_CXX14
test_allocator(test_allocator
&& a
) TEST_NOEXCEPT
: data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
) {
117 if (stats_
!= nullptr) {
121 assert(a
.data_
!= test_alloc_base::destructed_value
&& a
.id_
!= test_alloc_base::destructed_value
&&
122 "moving from destroyed allocator");
123 a
.data_
= test_alloc_base::moved_value
;
124 a
.id_
= test_alloc_base::moved_value
;
129 TEST_CONSTEXPR_CXX14
test_allocator(const test_allocator
<U
>& a
) TEST_NOEXCEPT
130 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
) {
131 if (stats_
!= nullptr) {
137 TEST_CONSTEXPR_CXX20
~test_allocator() TEST_NOEXCEPT
{
138 assert(data_
!= test_alloc_base::destructed_value
);
139 assert(id_
!= test_alloc_base::destructed_value
);
140 if (stats_
!= nullptr)
142 data_
= test_alloc_base::destructed_value
;
143 id_
= test_alloc_base::destructed_value
;
146 TEST_CONSTEXPR pointer
address(reference x
) const { return &x
; }
147 TEST_CONSTEXPR const_pointer
address(const_reference x
) const { return &x
; }
149 TEST_CONSTEXPR_CXX14 pointer
allocate(size_type n
, const void* = 0) {
150 assert(data_
!= test_alloc_base::destructed_value
);
151 if (stats_
!= nullptr) {
152 if (stats_
->time_to_throw
>= stats_
->throw_after
)
153 TEST_THROW(std::bad_alloc());
154 ++stats_
->time_to_throw
;
155 ++stats_
->alloc_count
;
157 return std::allocator
<value_type
>().allocate(n
);
160 TEST_CONSTEXPR_CXX14
void deallocate(pointer p
, size_type s
) {
161 assert(data_
!= test_alloc_base::destructed_value
);
162 if (stats_
!= nullptr)
163 --stats_
->alloc_count
;
164 std::allocator
<value_type
>().deallocate(p
, s
);
167 TEST_CONSTEXPR size_type
max_size() const TEST_NOEXCEPT
{ return UINT_MAX
/ sizeof(T
); }
169 #if TEST_STD_VER < 11
170 void construct(pointer p
, const T
& val
) { ::new (static_cast<void*>(p
)) T(val
); }
173 TEST_CONSTEXPR_CXX14
void construct(pointer p
, U
&& val
) {
174 ::new (static_cast<void*>(p
)) T(std::forward
<U
>(val
));
177 TEST_CONSTEXPR_CXX14
void destroy(pointer p
) { p
->~T(); }
178 TEST_CONSTEXPR
friend bool operator==(const test_allocator
& x
, const test_allocator
& y
) { return x
.data_
== y
.data_
; }
179 TEST_CONSTEXPR
friend bool operator!=(const test_allocator
& x
, const test_allocator
& y
) { return !(x
== y
); }
181 TEST_CONSTEXPR
int get_data() const { return data_
; }
182 TEST_CONSTEXPR
int get_id() const { return id_
; }
186 class non_default_test_allocator
{
188 test_allocator_statistics
* stats_
= nullptr;
191 friend class non_default_test_allocator
;
194 typedef unsigned size_type
;
195 typedef int difference_type
;
196 typedef T value_type
;
197 typedef value_type
* pointer
;
198 typedef const value_type
* const_pointer
;
199 typedef typename
std::add_lvalue_reference
<value_type
>::type reference
;
200 typedef typename
std::add_lvalue_reference
<const value_type
>::type const_reference
;
204 typedef non_default_test_allocator
<U
> other
;
208 explicit non_default_test_allocator(int i
, test_allocator_statistics
* stats
= nullptr) TEST_NOEXCEPT
209 : data_(i
), stats_(stats
) {
210 if (stats_
!= nullptr) {
216 non_default_test_allocator(const non_default_test_allocator
& a
) TEST_NOEXCEPT
: data_(a
.data_
), stats_(a
.stats_
) {
217 if (stats_
!= nullptr)
222 TEST_CONSTEXPR_CXX14
non_default_test_allocator(const non_default_test_allocator
<U
>& a
) TEST_NOEXCEPT
223 : data_(a
.data_
), stats_(a
.stats_
) {
224 if (stats_
!= nullptr)
228 TEST_CONSTEXPR_CXX20
~non_default_test_allocator() TEST_NOEXCEPT
{
229 assert(data_
!= test_alloc_base::destructed_value
);
230 if (stats_
!= nullptr)
232 data_
= test_alloc_base::destructed_value
;
235 TEST_CONSTEXPR pointer
address(reference x
) const { return &x
; }
236 TEST_CONSTEXPR const_pointer
address(const_reference x
) const { return &x
; }
238 TEST_CONSTEXPR_CXX20 pointer
allocate(size_type n
, const void* = nullptr) {
239 assert(data_
!= test_alloc_base::destructed_value
);
240 if (stats_
!= nullptr) {
241 if (stats_
->time_to_throw
>= stats_
->throw_after
)
242 TEST_THROW(std::bad_alloc());
243 ++stats_
->time_to_throw
;
244 ++stats_
->alloc_count
;
246 return std::allocator
<value_type
>().allocate(n
);
249 TEST_CONSTEXPR_CXX20
void deallocate(pointer p
, size_type n
) {
250 assert(data_
!= test_alloc_base::destructed_value
);
251 if (stats_
!= nullptr)
252 --stats_
->alloc_count
;
253 std::allocator
<value_type
>().deallocate(p
, n
);
256 TEST_CONSTEXPR size_type
max_size() const TEST_NOEXCEPT
{ return UINT_MAX
/ sizeof(T
); }
258 TEST_CONSTEXPR
friend bool operator==(const non_default_test_allocator
& x
, const non_default_test_allocator
& y
) {
259 return x
.data_
== y
.data_
;
262 TEST_CONSTEXPR
friend bool operator!=(const non_default_test_allocator
& x
, const non_default_test_allocator
& y
) {
268 class test_allocator
<void> {
271 test_allocator_statistics
* stats_
= nullptr;
274 friend class test_allocator
;
277 typedef unsigned size_type
;
278 typedef int difference_type
;
279 typedef void value_type
;
280 typedef value_type
* pointer
;
281 typedef const value_type
* const_pointer
;
285 typedef test_allocator
<U
> other
;
288 TEST_CONSTEXPR
test_allocator() TEST_NOEXCEPT
= default;
290 TEST_CONSTEXPR_CXX14
explicit test_allocator(test_allocator_statistics
* stats
) TEST_NOEXCEPT
: stats_(stats
) {}
292 TEST_CONSTEXPR
explicit test_allocator(int data
) TEST_NOEXCEPT
: data_(data
) {}
294 TEST_CONSTEXPR
explicit test_allocator(int data
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
295 : data_(data
), stats_(stats
)
298 TEST_CONSTEXPR
explicit test_allocator(int data
, int id
) : data_(data
), id_(id
) {}
300 TEST_CONSTEXPR_CXX14
explicit test_allocator(int data
, int id
, test_allocator_statistics
* stats
) TEST_NOEXCEPT
301 : data_(data
), id_(id
), stats_(stats
)
304 TEST_CONSTEXPR_CXX14
explicit test_allocator(const test_allocator
& a
) TEST_NOEXCEPT
305 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
)
309 TEST_CONSTEXPR_CXX14
test_allocator(const test_allocator
<U
>& a
) TEST_NOEXCEPT
310 : data_(a
.data_
), id_(a
.id_
), stats_(a
.stats_
)
313 TEST_CONSTEXPR_CXX20
~test_allocator() TEST_NOEXCEPT
{
314 data_
= test_alloc_base::destructed_value
;
315 id_
= test_alloc_base::destructed_value
;
318 TEST_CONSTEXPR
int get_id() const { return id_
; }
319 TEST_CONSTEXPR
int get_data() const { return data_
; }
321 TEST_CONSTEXPR
friend bool operator==(const test_allocator
& x
, const test_allocator
& y
) { return x
.data_
== y
.data_
; }
322 TEST_CONSTEXPR
friend bool operator!=(const test_allocator
& x
, const test_allocator
& y
) { return !(x
== y
); }
326 class other_allocator
{
330 friend class other_allocator
;
333 typedef T value_type
;
335 TEST_CONSTEXPR_CXX14
other_allocator() {}
336 TEST_CONSTEXPR_CXX14
explicit other_allocator(int i
) : data_(i
) {}
339 TEST_CONSTEXPR_CXX14
other_allocator(const other_allocator
<U
>& a
) : data_(a
.data_
) {}
341 TEST_CONSTEXPR_CXX20 T
* allocate(std::size_t n
) { return std::allocator
<value_type
>().allocate(n
); }
342 TEST_CONSTEXPR_CXX20
void deallocate(T
* p
, std::size_t s
) { std::allocator
<value_type
>().deallocate(p
, s
); }
344 TEST_CONSTEXPR_CXX14 other_allocator
select_on_container_copy_construction() const { return other_allocator(-2); }
346 TEST_CONSTEXPR_CXX14
friend bool operator==(const other_allocator
& x
, const other_allocator
& y
) {
347 return x
.data_
== y
.data_
;
350 TEST_CONSTEXPR_CXX14
friend bool operator!=(const other_allocator
& x
, const other_allocator
& y
) { return !(x
== y
); }
352 typedef std::true_type propagate_on_container_copy_assignment
;
353 typedef std::true_type propagate_on_container_move_assignment
;
354 typedef std::true_type propagate_on_container_swap
;
356 #if TEST_STD_VER < 11
357 std::size_t max_size() const { return UINT_MAX
/ sizeof(T
); }
361 #if TEST_STD_VER >= 11
365 template <typename T
>
366 class TaggingAllocator
;
369 // All constructors must be passed the Tag type.
371 // DefaultInsertable into vector<X, TaggingAllocator<X>>,
372 constexpr Tag_X(Ctor_Tag
) {}
373 // CopyInsertable into vector<X, TaggingAllocator<X>>,
374 constexpr Tag_X(Ctor_Tag
, const Tag_X
&) {}
375 // MoveInsertable into vector<X, TaggingAllocator<X>>, and
376 constexpr Tag_X(Ctor_Tag
, Tag_X
&&) {}
378 // EmplaceConstructible into vector<X, TaggingAllocator<X>> from args.
379 template <typename
... Args
>
380 constexpr Tag_X(Ctor_Tag
, Args
&&...) {}
382 // not DefaultConstructible, CopyConstructible or MoveConstructible.
384 Tag_X(const Tag_X
&) = delete;
385 Tag_X(Tag_X
&&) = delete;
388 TEST_CONSTEXPR_CXX14 Tag_X
& operator=(const Tag_X
&) { return *this; };
391 TEST_CONSTEXPR_CXX14 Tag_X
& operator=(Tag_X
&&) { return *this; };
395 // Erasable from vector<X, TaggingAllocator<X>>.
396 friend class TaggingAllocator
<Tag_X
>;
399 template <typename T
>
400 class TaggingAllocator
{
402 using value_type
= T
;
403 TaggingAllocator() = default;
405 template <typename U
>
406 constexpr TaggingAllocator(const TaggingAllocator
<U
>&){};
408 template <typename
... Args
>
409 void construct(Tag_X
* p
, Args
&&... args
) {
410 ::new ((void*)p
) Tag_X(Ctor_Tag
{}, std::forward
<Args
>(args
)...);
413 template <typename U
>
418 TEST_CONSTEXPR_CXX20 T
* allocate(std::size_t n
) { return std::allocator
<T
>{}.allocate(n
); }
419 TEST_CONSTEXPR_CXX20
void deallocate(T
* p
, std::size_t n
) { std::allocator
<T
>{}.deallocate(p
, n
); }
423 template <std::size_t MaxAllocs
>
424 struct limited_alloc_handle
{
425 std::size_t outstanding_
= 0;
426 void* last_alloc_
= nullptr;
429 TEST_CONSTEXPR_CXX20 T
* allocate(std::size_t N
) {
430 if (N
+ outstanding_
> MaxAllocs
)
431 TEST_THROW(std::bad_alloc());
432 last_alloc_
= std::allocator
<T
>().allocate(N
);
434 return static_cast<T
*>(last_alloc_
);
438 TEST_CONSTEXPR_CXX20
void deallocate(T
* ptr
, std::size_t N
) {
439 if (ptr
== last_alloc_
) {
440 last_alloc_
= nullptr;
441 assert(outstanding_
>= N
);
444 std::allocator
<T
>().deallocate(ptr
, N
);
450 class thread_unsafe_shared_ptr
{
452 thread_unsafe_shared_ptr() = default;
454 TEST_CONSTEXPR_CXX14
thread_unsafe_shared_ptr(const thread_unsafe_shared_ptr
& other
) : block(other
.block
) {
458 TEST_CONSTEXPR_CXX20
~thread_unsafe_shared_ptr() {
460 if (block
->ref_count
!= 0)
462 typedef std::allocator_traits
<std::allocator
<control_block
> > allocator_traits
;
463 std::allocator
<control_block
> alloc
;
464 allocator_traits::destroy(alloc
, block
);
465 allocator_traits::deallocate(alloc
, block
, 1);
468 TEST_CONSTEXPR
const T
& operator*() const { return block
->content
; }
469 TEST_CONSTEXPR
const T
* operator->() const { return &block
->content
; }
470 TEST_CONSTEXPR_CXX14 T
& operator*() { return block
->content
; }
471 TEST_CONSTEXPR_CXX14 T
* operator->() { return &block
->content
; }
472 TEST_CONSTEXPR_CXX14 T
* get() { return &block
->content
; }
473 TEST_CONSTEXPR
const T
* get() const { return &block
->content
; }
476 struct control_block
{
477 template <class... Args
>
478 TEST_CONSTEXPR
control_block(Args
... args
) : content(std::forward
<Args
>(args
)...) {}
479 size_t ref_count
= 1;
483 control_block
* block
= nullptr;
485 template <class U
, class... Args
>
486 friend TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr
<U
> make_thread_unsafe_shared(Args
...);
489 template <class T
, class... Args
>
490 TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr
<T
> make_thread_unsafe_shared(Args
... args
) {
491 typedef typename thread_unsafe_shared_ptr
<T
>::control_block control_block_type
;
492 typedef std::allocator_traits
<std::allocator
<control_block_type
> > allocator_traits
;
494 thread_unsafe_shared_ptr
<T
> ptr
;
495 std::allocator
<control_block_type
> alloc
;
496 ptr
.block
= allocator_traits::allocate(alloc
, 1);
497 allocator_traits::construct(alloc
, ptr
.block
, std::forward
<Args
>(args
)...);
501 } // namespace detail
503 template <class T
, std::size_t N
>
504 class limited_allocator
{
505 template <class U
, std::size_t UN
>
506 friend class limited_allocator
;
507 typedef limited_alloc_handle
<N
> BuffT
;
508 detail::thread_unsafe_shared_ptr
<BuffT
> handle_
;
511 typedef T value_type
;
512 typedef value_type
* pointer
;
513 typedef const value_type
* const_pointer
;
514 typedef value_type
& reference
;
515 typedef const value_type
& const_reference
;
516 typedef std::size_t size_type
;
517 typedef std::ptrdiff_t difference_type
;
521 typedef limited_allocator
<U
, N
> other
;
524 TEST_CONSTEXPR_CXX20
limited_allocator() : handle_(detail::make_thread_unsafe_shared
<BuffT
>()) {}
526 limited_allocator(limited_allocator
const&) = default;
529 TEST_CONSTEXPR
explicit limited_allocator(limited_allocator
<U
, N
> const& other
) : handle_(other
.handle_
) {}
531 limited_allocator
& operator=(const limited_allocator
&) = delete;
533 TEST_CONSTEXPR_CXX20 pointer
allocate(size_type n
) { return handle_
->template allocate
<T
>(n
); }
534 TEST_CONSTEXPR_CXX20
void deallocate(pointer p
, size_type n
) { handle_
->template deallocate
<T
>(p
, n
); }
535 TEST_CONSTEXPR size_type
max_size() const { return N
; }
536 TEST_CONSTEXPR BuffT
* getHandle() const { return handle_
.get(); }
539 template <class T
, class U
, std::size_t N
>
540 TEST_CONSTEXPR
inline bool operator==(limited_allocator
<T
, N
> const& LHS
, limited_allocator
<U
, N
> const& RHS
) {
541 return LHS
.getHandle() == RHS
.getHandle();
544 template <class T
, class U
, std::size_t N
>
545 TEST_CONSTEXPR
inline bool operator!=(limited_allocator
<T
, N
> const& LHS
, limited_allocator
<U
, N
> const& RHS
) {
546 return !(LHS
== RHS
);
549 #endif // TEST_ALLOCATOR_H