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 return last_construct_pointer
== p
&&
190 COMPARE_TYPEID(last_construct_alloc
, expectAlloc
) &&
191 COMPARE_TYPEID(last_construct_type
, expectTp
) &&
192 COMPARE_TYPEID(last_construct_args
, expectArgs
);
195 template <class Alloc
, class Tp
>
196 bool checkDestroy(Alloc
const&, Tp
*p
) const {
197 return last_destroy_pointer
== p
&&
198 last_destroy_alloc
== &makeTypeID
<Alloc
>() &&
199 last_destroy_type
== &makeTypeID
<Tp
>();
202 bool checkDestroyMatchesConstruct() const {
203 return last_destroy_pointer
== last_construct_pointer
&&
204 last_destroy_type
== last_construct_type
;
207 void countIsEqual() {
211 bool checkIsEqualCalledEq(int n
) const {
212 return is_equal_count
== n
;
215 DISALLOW_COPY(AllocController
);
218 template <class T
, size_t ID
>
219 class CountingAllocator
222 typedef T value_type
;
226 struct rebind
{ using other
= CountingAllocator
<U
, ID
>; };
228 CountingAllocator() = delete;
229 explicit CountingAllocator(AllocController
& PP
) : P(&PP
) {}
231 CountingAllocator(CountingAllocator
const& other
) : P(other
.P
) {
232 P
->copy_constructed
+= 1;
235 CountingAllocator(CountingAllocator
&& other
) : P(other
.P
) {
236 P
->move_constructed
+= 1;
240 CountingAllocator(CountingAllocator
<U
, ID
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
241 P
->copy_constructed
+= 1;
245 CountingAllocator(CountingAllocator
<U
, ID
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
246 P
->move_constructed
+= 1;
249 T
* allocate(std::size_t n
)
251 void* ret
= ::operator new(n
*sizeof(T
));
252 P
->countAlloc(ret
, n
*sizeof(T
), alignof(T
));
253 return static_cast<T
*>(ret
);
256 void deallocate(T
* p
, std::size_t n
)
258 void* vp
= static_cast<void*>(p
);
259 P
->countDealloc(vp
, n
*sizeof(T
), alignof(T
));
260 ::operator delete(vp
);
263 template <class U
, class ...Args
>
264 void construct(U
*p
, Args
&&... args
) {
265 ::new ((void*)p
) U(std::forward
<Args
>(args
)...);
266 P
->countConstruct
<Args
&&...>(*this, p
);
272 P
->countDestroy(*this, p
);
275 AllocController
& getController() const { return *P
; }
278 template <class Tp
, size_t XID
> friend class CountingAllocator
;
284 class CountingAllocator
<void, ID
>
287 typedef void* pointer
;
288 typedef const void* const_pointer
;
289 typedef void value_type
;
292 struct rebind
{ using other
= CountingAllocator
<U
, ID
>; };
294 CountingAllocator() = delete;
295 explicit CountingAllocator(AllocController
& PP
) : P(&PP
) {}
297 CountingAllocator(CountingAllocator
const& other
) : P(other
.P
) {
298 P
->copy_constructed
+= 1;
301 CountingAllocator(CountingAllocator
&& other
) : P(other
.P
) {
302 P
->move_constructed
+= 1;
306 CountingAllocator(CountingAllocator
<U
, ID
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
307 P
->copy_constructed
+= 1;
311 CountingAllocator(CountingAllocator
<U
, ID
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
312 P
->move_constructed
+= 1;
315 void construct(...) = delete;
316 void destroy(void*) = delete;
318 AllocController
& getController() const { return *P
; }
321 template <class Tp
, size_t> friend class CountingAllocator
;
325 template <class T
, class U
, size_t ID
>
326 inline bool operator==(CountingAllocator
<T
, ID
> const& x
,
327 CountingAllocator
<U
, ID
> const& y
) {
328 return &x
.getController() == &y
.getController();
331 template <class T
, class U
, size_t ID
>
332 inline bool operator!=(CountingAllocator
<T
, ID
> const& x
,
333 CountingAllocator
<U
, ID
> const& y
) {
338 class MinAlignedAllocator
341 typedef T value_type
;
344 MinAlignedAllocator() = delete;
346 explicit MinAlignedAllocator(AllocController
& R
) : P(&R
) {}
348 MinAlignedAllocator(MinAlignedAllocator
const& other
) : P(other
.P
) {
349 P
->copy_constructed
+= 1;
352 MinAlignedAllocator(MinAlignedAllocator
&& other
) : P(other
.P
) {
353 P
->move_constructed
+= 1;
357 MinAlignedAllocator(MinAlignedAllocator
<U
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
358 P
->copy_constructed
+= 1;
362 MinAlignedAllocator(MinAlignedAllocator
<U
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
363 P
->move_constructed
+= 1;
366 T
* allocate(std::size_t n
) {
367 char* aligned_ptr
= (char*)::operator new(alloc_size(n
*sizeof(T
)));
368 assert(is_max_aligned(aligned_ptr
));
370 char* unaligned_ptr
= aligned_ptr
+ alignof(T
);
371 assert(is_min_aligned(unaligned_ptr
));
373 P
->countAlloc(unaligned_ptr
, n
* sizeof(T
), alignof(T
));
375 return ((T
*)unaligned_ptr
);
378 void deallocate(T
* p
, std::size_t n
) {
379 assert(is_min_aligned(p
));
381 char* aligned_ptr
= ((char*)p
) - alignof(T
);
382 assert(is_max_aligned(aligned_ptr
));
384 P
->countDealloc(p
, n
*sizeof(T
), alignof(T
));
386 return ::operator delete(static_cast<void*>(aligned_ptr
));
389 template <class U
, class ...Args
>
390 void construct(U
*p
, Args
&&... args
) {
391 auto *c
= ::new ((void*)p
) U(std::forward
<Args
>(args
)...);
392 P
->countConstruct
<Args
&&...>(*this, p
);
398 P
->countDestroy(*this, p
);
401 AllocController
& getController() const { return *P
; }
404 static const std::size_t BlockSize
= alignof(std::max_align_t
);
406 static std::size_t alloc_size(std::size_t s
) {
407 std::size_t bytes
= (s
+ BlockSize
- 1) & ~(BlockSize
- 1);
409 assert(bytes
% BlockSize
== 0);
413 static bool is_max_aligned(void* p
) {
414 return reinterpret_cast<std::uintptr_t>(p
) % BlockSize
== 0;
417 static bool is_min_aligned(void* p
) {
418 if (alignof(T
) == BlockSize
) {
419 return is_max_aligned(p
);
421 return reinterpret_cast<std::uintptr_t>(p
) % BlockSize
== alignof(T
);
425 template <class Tp
> friend class MinAlignedAllocator
;
426 mutable AllocController
*P
;
430 template <class T
, class U
>
431 inline bool operator==(MinAlignedAllocator
<T
> const& x
,
432 MinAlignedAllocator
<U
> const& y
) {
433 return &x
.getController() == &y
.getController();
436 template <class T
, class U
>
437 inline bool operator!=(MinAlignedAllocator
<T
> const& x
,
438 MinAlignedAllocator
<U
> const& y
) {
446 typedef T value_type
;
448 NullAllocator() = delete;
449 explicit NullAllocator(AllocController
& PP
) : P(&PP
) {}
451 NullAllocator(NullAllocator
const& other
) : P(other
.P
) {
452 P
->copy_constructed
+= 1;
455 NullAllocator(NullAllocator
&& other
) : P(other
.P
) {
456 P
->move_constructed
+= 1;
460 NullAllocator(NullAllocator
<U
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
461 P
->copy_constructed
+= 1;
465 NullAllocator(NullAllocator
<U
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
466 P
->move_constructed
+= 1;
469 T
* allocate(std::size_t n
)
471 P
->countAlloc(nullptr, n
*sizeof(T
), alignof(T
));
475 void deallocate(T
* p
, std::size_t n
)
477 void* vp
= static_cast<void*>(p
);
478 P
->countDealloc(vp
, n
*sizeof(T
), alignof(T
));
481 AllocController
& getController() const { return *P
; }
484 template <class Tp
> friend class NullAllocator
;
488 template <class T
, class U
>
489 inline bool operator==(NullAllocator
<T
> const& x
,
490 NullAllocator
<U
> const& y
) {
491 return &x
.getController() == &y
.getController();
494 template <class T
, class U
>
495 inline bool operator!=(NullAllocator
<T
> const& x
,
496 NullAllocator
<U
> const& y
) {
501 #endif /* SUPPORT_CONTROLLED_ALLOCATORS_H */