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 SUPPORT_CONTROLLED_ALLOCATORS_H
10 #define SUPPORT_CONTROLLED_ALLOCATORS_H
13 #include <type_traits>
19 #include "test_macros.h"
23 #error This header requires C++11 or greater
26 struct AllocController
;
27 // 'AllocController' is a concrete type that instruments and controls the
28 // behavior of test allocators.
30 template <class T
, size_t ID
= 0>
31 class CountingAllocator
;
32 // 'CountingAllocator' is an basic implementation of the 'Allocator'
33 // requirements that use the 'AllocController' interface.
36 class MinAlignAllocator
;
37 // 'MinAlignAllocator' is an instrumented test type which implements the
38 // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never*
39 // returns a pointer to over-aligned storage. For example
40 // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned
45 // 'NullAllocator' is an instrumented test type which implements the
46 // 'Allocator' requirements except that 'allocator' and 'deallocate' are
50 #define DISALLOW_COPY(Type) \
51 Type(Type const&) = delete; \
52 Type& operator=(Type const&) = delete
54 constexpr std::size_t MaxAlignV
= alignof(std::max_align_t
);
56 struct TestException
{};
58 struct AllocController
{
59 int copy_constructed
= 0;
60 int move_constructed
= 0;
64 int dealloc_count
= 0;
65 int is_equal_count
= 0;
67 std::size_t alive_size
;
68 std::size_t allocated_size
;
69 std::size_t deallocated_size
;
71 std::size_t last_size
= 0;
72 std::size_t last_align
= 0;
73 void * last_pointer
= 0;
75 std::size_t last_alloc_size
= 0;
76 std::size_t last_alloc_align
= 0;
77 void * last_alloc_pointer
= nullptr;
79 std::size_t last_dealloc_size
= 0;
80 std::size_t last_dealloc_align
= 0;
81 void * last_dealloc_pointer
= nullptr;
83 bool throw_on_alloc
= false;
85 int construct_called
= 0;
86 void *last_construct_pointer
= nullptr;
87 TypeID
const* last_construct_alloc
= nullptr;
88 TypeID
const* last_construct_type
= nullptr;
89 TypeID
const* last_construct_args
= nullptr;
91 int destroy_called
= 0;
92 void *last_destroy_pointer
= nullptr;
93 TypeID
const* last_destroy_alloc
= nullptr;
94 TypeID
const* last_destroy_type
= nullptr;
96 AllocController() = default;
98 void countAlloc(void* p
, size_t s
, size_t a
) {
103 last_pointer
= last_alloc_pointer
= p
;
104 last_size
= last_alloc_size
= s
;
105 last_align
= last_alloc_align
= a
;
108 void countDealloc(void* p
, size_t s
, size_t a
) {
112 deallocated_size
+= s
;
113 last_pointer
= last_dealloc_pointer
= p
;
114 last_size
= last_dealloc_size
= s
;
115 last_align
= last_dealloc_align
= a
;
118 template <class ...Args
, class Alloc
, class Tp
>
119 void countConstruct(Alloc
const&, Tp
*p
) {
121 last_construct_pointer
= p
;
122 last_construct_alloc
= &makeTypeID
<Alloc
>();
123 last_construct_type
= &makeTypeID
<Tp
>();
124 last_construct_args
= &makeArgumentID
<Args
...>();
127 template <class Alloc
, class Tp
>
128 void countDestroy(Alloc
const&, Tp
*p
) {
130 last_destroy_alloc
= &makeTypeID
<Alloc
>();
131 last_destroy_type
= &makeTypeID
<Tp
>();
132 last_destroy_pointer
= p
;
135 void reset() { std::memset(this, 0, sizeof(*this)); }
136 void resetConstructDestroy() {
137 construct_called
= 0;
138 last_construct_pointer
= nullptr;
139 last_construct_alloc
= last_construct_args
= last_construct_type
= nullptr;
141 last_destroy_alloc
= nullptr;
142 last_destroy_pointer
= nullptr;
145 bool checkAlloc(void* p
, size_t s
, size_t a
) const {
146 return p
== last_alloc_pointer
&&
147 s
== last_alloc_size
&&
148 a
== last_alloc_align
;
151 bool checkAlloc(void* p
, size_t s
) const {
152 return p
== last_alloc_pointer
&&
153 s
== last_alloc_size
;
156 bool checkAllocAtLeast(void* p
, size_t s
, size_t a
) const {
157 return p
== last_alloc_pointer
&&
158 s
<= last_alloc_size
&&
159 a
<= last_alloc_align
;
162 bool checkAllocAtLeast(void* p
, size_t s
) const {
163 return p
== last_alloc_pointer
&&
164 s
<= last_alloc_size
;
167 bool checkDealloc(void* p
, size_t s
, size_t a
) const {
168 return p
== last_dealloc_pointer
&&
169 s
== last_dealloc_size
&&
170 a
== last_dealloc_align
;
173 bool checkDealloc(void* p
, size_t s
) const {
174 return p
== last_dealloc_pointer
&&
175 s
== last_dealloc_size
;
178 bool checkDeallocMatchesAlloc() const {
179 return last_dealloc_pointer
== last_alloc_pointer
&&
180 last_dealloc_size
== last_alloc_size
&&
181 last_dealloc_align
== last_alloc_align
;
184 template <class ...Args
, class Alloc
, class Tp
>
185 bool checkConstruct(Alloc
const&, Tp
*p
) const {
186 auto expectAlloc
= &makeTypeID
<Alloc
>();
187 auto expectTp
= &makeTypeID
<Tp
>();
188 auto expectArgs
= &makeArgumentID
<Args
...>();
189 if (last_construct_pointer
!= p
)
191 if (last_construct_alloc
!= expectAlloc
)
193 if (last_construct_type
!= expectTp
)
195 if (last_construct_args
!= expectArgs
)
200 template <class Alloc
, class Tp
>
201 bool checkDestroy(Alloc
const&, Tp
*p
) const {
202 return last_destroy_pointer
== p
&&
203 last_destroy_alloc
== &makeTypeID
<Alloc
>() &&
204 last_destroy_type
== &makeTypeID
<Tp
>();
207 bool checkDestroyMatchesConstruct() const {
208 return last_destroy_pointer
== last_construct_pointer
&&
209 last_destroy_type
== last_construct_type
;
212 void countIsEqual() {
216 bool checkIsEqualCalledEq(int n
) const {
217 return is_equal_count
== n
;
220 DISALLOW_COPY(AllocController
);
223 template <class T
, size_t ID
>
224 class CountingAllocator
227 typedef T value_type
;
231 struct rebind
{ using other
= CountingAllocator
<U
, ID
>; };
233 CountingAllocator() = delete;
234 explicit CountingAllocator(AllocController
& PP
) : P(&PP
) {}
236 CountingAllocator(CountingAllocator
const& other
) : P(other
.P
) {
237 P
->copy_constructed
+= 1;
240 CountingAllocator(CountingAllocator
&& other
) : P(other
.P
) {
241 P
->move_constructed
+= 1;
245 CountingAllocator(CountingAllocator
<U
, ID
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
246 P
->copy_constructed
+= 1;
250 CountingAllocator(CountingAllocator
<U
, ID
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
251 P
->move_constructed
+= 1;
254 T
* allocate(std::size_t n
)
256 void* ret
= ::operator new(n
*sizeof(T
));
257 P
->countAlloc(ret
, n
*sizeof(T
), alignof(T
));
258 return static_cast<T
*>(ret
);
261 void deallocate(T
* p
, std::size_t n
)
263 void* vp
= static_cast<void*>(p
);
264 P
->countDealloc(vp
, n
*sizeof(T
), alignof(T
));
265 ::operator delete(vp
);
268 template <class U
, class ...Args
>
269 void construct(U
*p
, Args
&&... args
) {
270 ::new ((void*)p
) U(std::forward
<Args
>(args
)...);
271 P
->countConstruct
<Args
&&...>(*this, p
);
277 P
->countDestroy(*this, p
);
280 AllocController
& getController() const { return *P
; }
283 template <class Tp
, size_t XID
> friend class CountingAllocator
;
289 class CountingAllocator
<void, ID
>
292 typedef void* pointer
;
293 typedef const void* const_pointer
;
294 typedef void value_type
;
297 struct rebind
{ using other
= CountingAllocator
<U
, ID
>; };
299 CountingAllocator() = delete;
300 explicit CountingAllocator(AllocController
& PP
) : P(&PP
) {}
302 CountingAllocator(CountingAllocator
const& other
) : P(other
.P
) {
303 P
->copy_constructed
+= 1;
306 CountingAllocator(CountingAllocator
&& other
) : P(other
.P
) {
307 P
->move_constructed
+= 1;
311 CountingAllocator(CountingAllocator
<U
, ID
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
312 P
->copy_constructed
+= 1;
316 CountingAllocator(CountingAllocator
<U
, ID
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
317 P
->move_constructed
+= 1;
320 void construct(...) = delete;
321 void destroy(void*) = delete;
323 AllocController
& getController() const { return *P
; }
326 template <class Tp
, size_t> friend class CountingAllocator
;
330 template <class T
, class U
, size_t ID
>
331 inline bool operator==(CountingAllocator
<T
, ID
> const& x
,
332 CountingAllocator
<U
, ID
> const& y
) {
333 return &x
.getController() == &y
.getController();
336 template <class T
, class U
, size_t ID
>
337 inline bool operator!=(CountingAllocator
<T
, ID
> const& x
,
338 CountingAllocator
<U
, ID
> const& y
) {
343 class MinAlignedAllocator
346 typedef T value_type
;
349 MinAlignedAllocator() = delete;
351 explicit MinAlignedAllocator(AllocController
& R
) : P(&R
) {}
353 MinAlignedAllocator(MinAlignedAllocator
const& other
) : P(other
.P
) {
354 P
->copy_constructed
+= 1;
357 MinAlignedAllocator(MinAlignedAllocator
&& other
) : P(other
.P
) {
358 P
->move_constructed
+= 1;
362 MinAlignedAllocator(MinAlignedAllocator
<U
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
363 P
->copy_constructed
+= 1;
367 MinAlignedAllocator(MinAlignedAllocator
<U
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
368 P
->move_constructed
+= 1;
371 T
* allocate(std::size_t n
) {
372 char* aligned_ptr
= (char*)::operator new(alloc_size(n
*sizeof(T
)));
373 assert(is_max_aligned(aligned_ptr
));
375 char* unaligned_ptr
= aligned_ptr
+ alignof(T
);
376 assert(is_min_aligned(unaligned_ptr
));
378 P
->countAlloc(unaligned_ptr
, n
* sizeof(T
), alignof(T
));
380 return ((T
*)unaligned_ptr
);
383 void deallocate(T
* p
, std::size_t n
) {
384 assert(is_min_aligned(p
));
386 char* aligned_ptr
= ((char*)p
) - alignof(T
);
387 assert(is_max_aligned(aligned_ptr
));
389 P
->countDealloc(p
, n
*sizeof(T
), alignof(T
));
391 return ::operator delete(static_cast<void*>(aligned_ptr
));
394 template <class U
, class ...Args
>
395 void construct(U
*p
, Args
&&... args
) {
396 auto *c
= ::new ((void*)p
) U(std::forward
<Args
>(args
)...);
397 P
->countConstruct
<Args
&&...>(*this, p
);
403 P
->countDestroy(*this, p
);
406 AllocController
& getController() const { return *P
; }
409 static const std::size_t BlockSize
= alignof(std::max_align_t
);
411 static std::size_t alloc_size(std::size_t s
) {
412 std::size_t bytes
= (s
+ BlockSize
- 1) & ~(BlockSize
- 1);
414 assert(bytes
% BlockSize
== 0);
418 static bool is_max_aligned(void* p
) {
419 return reinterpret_cast<std::uintptr_t>(p
) % BlockSize
== 0;
422 static bool is_min_aligned(void* p
) {
423 if (alignof(T
) == BlockSize
) {
424 return is_max_aligned(p
);
426 return reinterpret_cast<std::uintptr_t>(p
) % BlockSize
== alignof(T
);
430 template <class Tp
> friend class MinAlignedAllocator
;
431 mutable AllocController
*P
;
435 template <class T
, class U
>
436 inline bool operator==(MinAlignedAllocator
<T
> const& x
,
437 MinAlignedAllocator
<U
> const& y
) {
438 return &x
.getController() == &y
.getController();
441 template <class T
, class U
>
442 inline bool operator!=(MinAlignedAllocator
<T
> const& x
,
443 MinAlignedAllocator
<U
> const& y
) {
451 typedef T value_type
;
453 NullAllocator() = delete;
454 explicit NullAllocator(AllocController
& PP
) : P(&PP
) {}
456 NullAllocator(NullAllocator
const& other
) : P(other
.P
) {
457 P
->copy_constructed
+= 1;
460 NullAllocator(NullAllocator
&& other
) : P(other
.P
) {
461 P
->move_constructed
+= 1;
465 NullAllocator(NullAllocator
<U
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
466 P
->copy_constructed
+= 1;
470 NullAllocator(NullAllocator
<U
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
471 P
->move_constructed
+= 1;
474 T
* allocate(std::size_t n
)
476 P
->countAlloc(nullptr, n
*sizeof(T
), alignof(T
));
480 void deallocate(T
* p
, std::size_t n
)
482 void* vp
= static_cast<void*>(p
);
483 P
->countDealloc(vp
, n
*sizeof(T
), alignof(T
));
486 AllocController
& getController() const { return *P
; }
489 template <class Tp
> friend class NullAllocator
;
493 template <class T
, class U
>
494 inline bool operator==(NullAllocator
<T
> const& x
,
495 NullAllocator
<U
> const& y
) {
496 return &x
.getController() == &y
.getController();
499 template <class T
, class U
>
500 inline bool operator!=(NullAllocator
<T
> const& x
,
501 NullAllocator
<U
> const& y
) {
506 #endif /* SUPPORT_CONTROLLED_ALLOCATORS_H */