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>
21 #include "test_macros.h"
25 #error This header requires C++11 or greater
28 struct AllocController
;
29 // 'AllocController' is a concrete type that instruments and controls the
30 // behavior of test allocators.
32 template <class T
, std::size_t ID
= 0>
33 class CountingAllocator
;
34 // 'CountingAllocator' is an basic implementation of the 'Allocator'
35 // requirements that use the 'AllocController' interface.
38 class MinAlignAllocator
;
39 // 'MinAlignAllocator' is an instrumented test type which implements the
40 // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never*
41 // returns a pointer to over-aligned storage. For example
42 // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned
47 // 'NullAllocator' is an instrumented test type which implements the
48 // 'Allocator' requirements except that 'allocator' and 'deallocate' are
52 #define DISALLOW_COPY(Type) \
53 Type(Type const&) = delete; \
54 Type& operator=(Type const&) = delete
56 constexpr std::size_t MaxAlignV
= alignof(std::max_align_t
);
58 struct TestException
{};
60 struct AllocController
{
61 int copy_constructed
= 0;
62 int move_constructed
= 0;
66 int dealloc_count
= 0;
67 int is_equal_count
= 0;
69 std::size_t alive_size
;
70 std::size_t allocated_size
;
71 std::size_t deallocated_size
;
73 std::size_t last_size
= 0;
74 std::size_t last_align
= 0;
75 void * last_pointer
= 0;
77 std::size_t last_alloc_size
= 0;
78 std::size_t last_alloc_align
= 0;
79 void * last_alloc_pointer
= nullptr;
81 std::size_t last_dealloc_size
= 0;
82 std::size_t last_dealloc_align
= 0;
83 void * last_dealloc_pointer
= nullptr;
85 bool throw_on_alloc
= false;
87 int construct_called
= 0;
88 void *last_construct_pointer
= nullptr;
89 TypeID
const* last_construct_alloc
= nullptr;
90 TypeID
const* last_construct_type
= nullptr;
91 TypeID
const* last_construct_args
= nullptr;
93 int destroy_called
= 0;
94 void *last_destroy_pointer
= nullptr;
95 TypeID
const* last_destroy_alloc
= nullptr;
96 TypeID
const* last_destroy_type
= nullptr;
98 AllocController() = default;
100 void countAlloc(void* p
, std::size_t s
, size_t a
) {
105 last_pointer
= last_alloc_pointer
= p
;
106 last_size
= last_alloc_size
= s
;
107 last_align
= last_alloc_align
= a
;
110 void countDealloc(void* p
, std::size_t s
, size_t a
) {
114 deallocated_size
+= s
;
115 last_pointer
= last_dealloc_pointer
= p
;
116 last_size
= last_dealloc_size
= s
;
117 last_align
= last_dealloc_align
= a
;
120 template <class ...Args
, class Alloc
, class Tp
>
121 void countConstruct(Alloc
const&, Tp
*p
) {
123 last_construct_pointer
= p
;
124 last_construct_alloc
= &makeTypeID
<Alloc
>();
125 last_construct_type
= &makeTypeID
<Tp
>();
126 last_construct_args
= &makeArgumentID
<Args
...>();
129 template <class Alloc
, class Tp
>
130 void countDestroy(Alloc
const&, Tp
*p
) {
132 last_destroy_alloc
= &makeTypeID
<Alloc
>();
133 last_destroy_type
= &makeTypeID
<Tp
>();
134 last_destroy_pointer
= p
;
137 void reset() { std::memset(this, 0, sizeof(*this)); }
138 void resetConstructDestroy() {
139 construct_called
= 0;
140 last_construct_pointer
= nullptr;
141 last_construct_alloc
= last_construct_args
= last_construct_type
= nullptr;
143 last_destroy_alloc
= nullptr;
144 last_destroy_pointer
= nullptr;
147 bool checkAlloc(void* p
, std::size_t s
, size_t a
) const {
148 return p
== last_alloc_pointer
&&
149 s
== last_alloc_size
&&
150 a
== last_alloc_align
;
153 bool checkAlloc(void* p
, std::size_t s
) const {
154 return p
== last_alloc_pointer
&&
155 s
== last_alloc_size
;
158 bool checkAllocAtLeast(void* p
, std::size_t s
, size_t a
) const {
159 return p
== last_alloc_pointer
&&
160 s
<= last_alloc_size
&&
161 a
<= last_alloc_align
;
164 bool checkAllocAtLeast(void* p
, std::size_t s
) const {
165 return p
== last_alloc_pointer
&&
166 s
<= last_alloc_size
;
169 bool checkDealloc(void* p
, std::size_t s
, size_t a
) const {
170 return p
== last_dealloc_pointer
&&
171 s
== last_dealloc_size
&&
172 a
== last_dealloc_align
;
175 bool checkDealloc(void* p
, std::size_t s
) const {
176 return p
== last_dealloc_pointer
&&
177 s
== last_dealloc_size
;
180 bool checkDeallocMatchesAlloc() const {
181 return last_dealloc_pointer
== last_alloc_pointer
&&
182 last_dealloc_size
== last_alloc_size
&&
183 last_dealloc_align
== last_alloc_align
;
186 template <class ...Args
, class Alloc
, class Tp
>
187 bool checkConstruct(Alloc
const&, Tp
*p
) const {
188 auto expectAlloc
= &makeTypeID
<Alloc
>();
189 auto expectTp
= &makeTypeID
<Tp
>();
190 auto expectArgs
= &makeArgumentID
<Args
...>();
191 if (last_construct_pointer
!= p
)
193 if (last_construct_alloc
!= expectAlloc
)
195 if (last_construct_type
!= expectTp
)
197 if (last_construct_args
!= expectArgs
)
202 template <class Alloc
, class Tp
>
203 bool checkDestroy(Alloc
const&, Tp
*p
) const {
204 return last_destroy_pointer
== p
&&
205 last_destroy_alloc
== &makeTypeID
<Alloc
>() &&
206 last_destroy_type
== &makeTypeID
<Tp
>();
209 bool checkDestroyMatchesConstruct() const {
210 return last_destroy_pointer
== last_construct_pointer
&&
211 last_destroy_type
== last_construct_type
;
214 void countIsEqual() {
218 bool checkIsEqualCalledEq(int n
) const {
219 return is_equal_count
== n
;
222 DISALLOW_COPY(AllocController
);
225 template <class T
, std::size_t ID
>
226 class CountingAllocator
229 typedef T value_type
;
233 struct rebind
{ using other
= CountingAllocator
<U
, ID
>; };
235 CountingAllocator() = delete;
236 explicit CountingAllocator(AllocController
& PP
) : P(&PP
) {}
238 CountingAllocator(CountingAllocator
const& other
) : P(other
.P
) {
239 P
->copy_constructed
+= 1;
242 CountingAllocator(CountingAllocator
&& other
) : P(other
.P
) {
243 P
->move_constructed
+= 1;
247 CountingAllocator(CountingAllocator
<U
, ID
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
248 P
->copy_constructed
+= 1;
252 CountingAllocator(CountingAllocator
<U
, ID
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
253 P
->move_constructed
+= 1;
256 T
* allocate(std::size_t n
)
258 void* ret
= ::operator new(n
*sizeof(T
));
259 P
->countAlloc(ret
, n
*sizeof(T
), alignof(T
));
260 return static_cast<T
*>(ret
);
263 void deallocate(T
* p
, std::size_t n
)
265 void* vp
= static_cast<void*>(p
);
266 P
->countDealloc(vp
, n
*sizeof(T
), alignof(T
));
267 ::operator delete(vp
);
270 template <class U
, class ...Args
>
271 void construct(U
*p
, Args
&&... args
) {
272 ::new ((void*)p
) U(std::forward
<Args
>(args
)...);
273 P
->countConstruct
<Args
&&...>(*this, p
);
279 P
->countDestroy(*this, p
);
282 AllocController
& getController() const { return *P
; }
285 template <class Tp
, std::size_t XID
> friend class CountingAllocator
;
290 template <std::size_t ID
>
291 class CountingAllocator
<void, ID
>
294 typedef void* pointer
;
295 typedef const void* const_pointer
;
296 typedef void value_type
;
299 struct rebind
{ using other
= CountingAllocator
<U
, ID
>; };
301 CountingAllocator() = delete;
302 explicit CountingAllocator(AllocController
& PP
) : P(&PP
) {}
304 CountingAllocator(CountingAllocator
const& other
) : P(other
.P
) {
305 P
->copy_constructed
+= 1;
308 CountingAllocator(CountingAllocator
&& other
) : P(other
.P
) {
309 P
->move_constructed
+= 1;
313 CountingAllocator(CountingAllocator
<U
, ID
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
314 P
->copy_constructed
+= 1;
318 CountingAllocator(CountingAllocator
<U
, ID
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
319 P
->move_constructed
+= 1;
322 void construct(...) = delete;
323 void destroy(void*) = delete;
325 AllocController
& getController() const { return *P
; }
328 template <class Tp
, std::size_t> friend class CountingAllocator
;
332 template <class T
, class U
, std::size_t ID
>
333 inline bool operator==(CountingAllocator
<T
, ID
> const& x
,
334 CountingAllocator
<U
, ID
> const& y
) {
335 return &x
.getController() == &y
.getController();
338 template <class T
, class U
, std::size_t ID
>
339 inline bool operator!=(CountingAllocator
<T
, ID
> const& x
,
340 CountingAllocator
<U
, ID
> const& y
) {
345 class MinAlignedAllocator
348 typedef T value_type
;
351 MinAlignedAllocator() = delete;
353 explicit MinAlignedAllocator(AllocController
& R
) : P(&R
) {}
355 MinAlignedAllocator(MinAlignedAllocator
const& other
) : P(other
.P
) {
356 P
->copy_constructed
+= 1;
359 MinAlignedAllocator(MinAlignedAllocator
&& other
) : P(other
.P
) {
360 P
->move_constructed
+= 1;
364 MinAlignedAllocator(MinAlignedAllocator
<U
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
365 P
->copy_constructed
+= 1;
369 MinAlignedAllocator(MinAlignedAllocator
<U
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
370 P
->move_constructed
+= 1;
373 T
* allocate(std::size_t n
) {
374 char* aligned_ptr
= (char*)::operator new(alloc_size(n
*sizeof(T
)));
375 assert(is_max_aligned(aligned_ptr
));
377 char* unaligned_ptr
= aligned_ptr
+ alignof(T
);
378 assert(is_min_aligned(unaligned_ptr
));
380 P
->countAlloc(unaligned_ptr
, n
* sizeof(T
), alignof(T
));
382 return ((T
*)unaligned_ptr
);
385 void deallocate(T
* p
, std::size_t n
) {
386 assert(is_min_aligned(p
));
388 char* aligned_ptr
= ((char*)p
) - alignof(T
);
389 assert(is_max_aligned(aligned_ptr
));
391 P
->countDealloc(p
, n
*sizeof(T
), alignof(T
));
393 return ::operator delete(static_cast<void*>(aligned_ptr
));
396 template <class U
, class ...Args
>
397 void construct(U
*p
, Args
&&... args
) {
398 auto *c
= ::new ((void*)p
) U(std::forward
<Args
>(args
)...);
399 P
->countConstruct
<Args
&&...>(*this, p
);
405 P
->countDestroy(*this, p
);
408 AllocController
& getController() const { return *P
; }
411 static const std::size_t BlockSize
= alignof(std::max_align_t
);
413 static std::size_t alloc_size(std::size_t s
) {
414 std::size_t bytes
= (s
+ BlockSize
- 1) & ~(BlockSize
- 1);
416 assert(bytes
% BlockSize
== 0);
420 static bool is_max_aligned(void* p
) {
421 return reinterpret_cast<std::uintptr_t>(p
) % BlockSize
== 0;
424 static bool is_min_aligned(void* p
) {
425 if (alignof(T
) == BlockSize
) {
426 return is_max_aligned(p
);
428 return reinterpret_cast<std::uintptr_t>(p
) % BlockSize
== alignof(T
);
432 template <class Tp
> friend class MinAlignedAllocator
;
433 mutable AllocController
*P
;
437 template <class T
, class U
>
438 inline bool operator==(MinAlignedAllocator
<T
> const& x
,
439 MinAlignedAllocator
<U
> const& y
) {
440 return &x
.getController() == &y
.getController();
443 template <class T
, class U
>
444 inline bool operator!=(MinAlignedAllocator
<T
> const& x
,
445 MinAlignedAllocator
<U
> const& y
) {
453 typedef T value_type
;
455 NullAllocator() = delete;
456 explicit NullAllocator(AllocController
& PP
) : P(&PP
) {}
458 NullAllocator(NullAllocator
const& other
) : P(other
.P
) {
459 P
->copy_constructed
+= 1;
462 NullAllocator(NullAllocator
&& other
) : P(other
.P
) {
463 P
->move_constructed
+= 1;
467 NullAllocator(NullAllocator
<U
> const& other
) TEST_NOEXCEPT
: P(other
.P
) {
468 P
->copy_constructed
+= 1;
472 NullAllocator(NullAllocator
<U
>&& other
) TEST_NOEXCEPT
: P(other
.P
) {
473 P
->move_constructed
+= 1;
476 T
* allocate(std::size_t n
)
478 P
->countAlloc(nullptr, n
*sizeof(T
), alignof(T
));
482 void deallocate(T
* p
, std::size_t n
)
484 void* vp
= static_cast<void*>(p
);
485 P
->countDealloc(vp
, n
*sizeof(T
), alignof(T
));
488 AllocController
& getController() const { return *P
; }
491 template <class Tp
> friend class NullAllocator
;
495 template <class T
, class U
>
496 inline bool operator==(NullAllocator
<T
> const& x
,
497 NullAllocator
<U
> const& y
) {
498 return &x
.getController() == &y
.getController();
501 template <class T
, class U
>
502 inline bool operator!=(NullAllocator
<T
> const& x
,
503 NullAllocator
<U
> const& y
) {
508 #endif /* SUPPORT_CONTROLLED_ALLOCATORS_H */