blob: 4d5e74a357923e83d1997bd56eb0f6657e43960b [file] [log] [blame]
Eric Fiselier43a7f2c2016-12-09 09:51:09 +00001//===----------------------------------------------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is dual licensed under the MIT and the University of Illinois Open
6// Source Licenses. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef SUPPORT_CONTROLLED_ALLOCATORS_HPP
11#define SUPPORT_CONTROLLED_ALLOCATORS_HPP
12
13#include <memory>
14#include <type_traits>
15#include <cstddef>
16#include <cstdlib>
17#include <cstring>
18#include <cstdint>
19#include <cassert>
20#include "test_macros.h"
21#include "type_id.h"
22
23struct AllocController;
24 // 'AllocController' is a concrete type that instruments and controls the
25 // behavior of of test allocators.
26
27template <class T, size_t ID = 0>
28class CountingAllocator;
29 // 'CountingAllocator' is an basic implementation of the 'Allocator'
30 // requirements that use the 'AllocController' interface.
31
32template <class T>
33class MinAlignAllocator;
34 // 'MinAlignAllocator' is an instrumented test type which implements the
35 // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never*
36 // returns a pointer to over-aligned storage. For example
37 // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned
38 // pointer.
39
40template <class T>
41class NullAllocator;
42 // 'NullAllocator' is an instrumented test type which implements the
43 // 'Allocator' requirements except that 'allocator' and 'deallocate' are
44 // nops.
45
46
47#define DISALLOW_COPY(Type) \
48 Type(Type const&) = delete; \
49 Type& operator=(Type const&) = delete
50
51constexpr std::size_t MaxAlignV = alignof(std::max_align_t);
52
53struct TestException {};
54
55struct AllocController {
56 int copy_constructed = 0;
57 int move_constructed = 0;
58
59 int alive = 0;
60 int alloc_count = 0;
61 int dealloc_count = 0;
62 int is_equal_count = 0;
63
64 std::size_t alive_size;
65 std::size_t allocated_size;
66 std::size_t deallocated_size;
67
68 std::size_t last_size = 0;
69 std::size_t last_align = 0;
70 void * last_pointer = 0;
71
72 std::size_t last_alloc_size = 0;
73 std::size_t last_alloc_align = 0;
74 void * last_alloc_pointer = nullptr;
75
76 std::size_t last_dealloc_size = 0;
77 std::size_t last_dealloc_align = 0;
78 void * last_dealloc_pointer = nullptr;
79
80 bool throw_on_alloc = false;
81
82 int construct_called = 0;
83 void *last_construct_pointer = nullptr;
84 TypeID const* last_construct_alloc = nullptr;
85 TypeID const* last_construct_type = nullptr;
86 TypeID const* last_construct_args = nullptr;
87
88 int destroy_called = 0;
89 void *last_destroy_pointer = nullptr;
90 TypeID const* last_destroy_alloc = nullptr;
91 TypeID const* last_destroy_type = nullptr;
92
93 AllocController() = default;
94
95 void countAlloc(void* p, size_t s, size_t a) {
96 ++alive;
97 ++alloc_count;
98 alive_size += s;
99 allocated_size += s;
100 last_pointer = last_alloc_pointer = p;
101 last_size = last_alloc_size = s;
102 last_align = last_alloc_align = a;
103 }
104
105 void countDealloc(void* p, size_t s, size_t a) {
106 --alive;
107 ++dealloc_count;
108 alive_size -= s;
109 deallocated_size += s;
110 last_pointer = last_dealloc_pointer = p;
111 last_size = last_dealloc_size = s;
112 last_align = last_dealloc_align = a;
113 }
114
115 template <class ...Args, class Alloc, class Tp>
116 void countConstruct(Alloc const& a, Tp *p) {
117 ++construct_called;
118 last_construct_pointer = p;
119 last_construct_alloc = &makeTypeID<Alloc>();
120 last_construct_type = &makeTypeID<Tp>();
121 last_construct_args = &makeArgumentID<Args...>();
122 }
123
124 template <class Alloc, class Tp>
125 void countDestroy(Alloc const& a, Tp *p) {
126 ++destroy_called;
127 last_destroy_alloc = &makeTypeID<Alloc>();
128 last_destroy_type = &makeTypeID<Tp>();
129 last_destroy_pointer = p;
130 }
131
132 void reset() { std::memset(this, 0, sizeof(*this)); }
133 void resetConstructDestroy() {
134 construct_called = 0;
135 last_construct_pointer = nullptr;
136 last_construct_alloc = last_construct_args = last_construct_type = nullptr;
137 destroy_called = 0;
138 last_destroy_alloc = nullptr;
139 last_destroy_pointer = nullptr;
140 }
141public:
142 bool checkAlloc(void* p, size_t s, size_t a) const {
143 return p == last_alloc_pointer &&
144 s == last_alloc_size &&
145 a == last_alloc_align;
146 }
147
148 bool checkAlloc(void* p, size_t s) const {
149 return p == last_alloc_pointer &&
150 s == last_alloc_size;
151 }
152
153 bool checkAllocAtLeast(void* p, size_t s, size_t a) const {
154 return p == last_alloc_pointer &&
155 s <= last_alloc_size &&
156 a <= last_alloc_align;
157 }
158
159 bool checkAllocAtLeast(void* p, size_t s) const {
160 return p == last_alloc_pointer &&
161 s <= last_alloc_size;
162 }
163
164 bool checkDealloc(void* p, size_t s, size_t a) const {
165 return p == last_dealloc_pointer &&
166 s == last_dealloc_size &&
167 a == last_dealloc_align;
168 }
169
170 bool checkDealloc(void* p, size_t s) const {
171 return p == last_dealloc_pointer &&
172 s == last_dealloc_size;
173 }
174
175 bool checkDeallocMatchesAlloc() const {
176 return last_dealloc_pointer == last_alloc_pointer &&
177 last_dealloc_size == last_alloc_size &&
178 last_dealloc_align == last_alloc_align;
179 }
180
181 template <class ...Args, class Alloc, class Tp>
182 bool checkConstruct(Alloc const&, Tp *p) const {
183 auto expectAlloc = &makeTypeID<Alloc>();
184 auto expectTp = &makeTypeID<Tp>();
185 auto expectArgs = &makeArgumentID<Args...>();
186 return last_construct_pointer == p &&
187 COMPARE_TYPEID(last_construct_alloc, expectAlloc) &&
188 COMPARE_TYPEID(last_construct_type, expectTp) &&
189 COMPARE_TYPEID(last_construct_args, expectArgs);
190 }
191
192 template <class Alloc, class Tp>
193 bool checkDestroy(Alloc const&, Tp *p) const {
194 return last_destroy_pointer == p &&
195 last_destroy_alloc == &makeTypeID<Alloc>() &&
196 last_destroy_type == &makeTypeID<Tp>();
197 }
198
199 bool checkDestroyMatchesConstruct() const {
200 return last_destroy_pointer == last_construct_pointer &&
201 last_destroy_type == last_construct_type;
202 }
203
204 void countIsEqual() {
205 ++is_equal_count;
206 }
207
208 bool checkIsEqualCalledEq(int n) const {
209 return is_equal_count == n;
210 }
211private:
212 DISALLOW_COPY(AllocController);
213};
214
215template <class T, size_t ID>
216class CountingAllocator
217{
218public:
219 typedef T value_type;
220 typedef T* pointer;
221
222 template <class U>
223 struct rebind { using other = CountingAllocator<U, ID>; };
224
225 CountingAllocator() = delete;
226 explicit CountingAllocator(AllocController& PP) : P(&PP) {}
227
228 CountingAllocator(CountingAllocator const& other) : P(other.P) {
229 P->copy_constructed += 1;
230 }
231
232 CountingAllocator(CountingAllocator&& other) : P(other.P) {
233 P->move_constructed += 1;
234 }
235
236 template <class U>
237 CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) {
238 P->copy_constructed += 1;
239 }
240
241 template <class U>
242 CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) {
243 P->move_constructed += 1;
244 }
245
246 T* allocate(std::size_t n)
247 {
248 void* ret = ::operator new(n*sizeof(T));
249 P->countAlloc(ret, n*sizeof(T), alignof(T));
250 return static_cast<T*>(ret);
251 }
252
253 void deallocate(T* p, std::size_t n)
254 {
255 void* vp = static_cast<void*>(p);
256 P->countDealloc(vp, n*sizeof(T), alignof(T));
257 ::operator delete(vp);
258 }
259
260 template <class U, class ...Args>
261 void construct(U *p, Args&&... args) {
262 auto *c = ::new ((void*)p) U(std::forward<Args>(args)...);
263 P->countConstruct<Args&&...>(*this, p);
264 }
265
266 template <class U>
267 void destroy(U* p) {
268 p->~U();
269 P->countDestroy(*this, p);
270 }
271
272 AllocController& getController() const { return *P; }
273
274private:
275 template <class Tp, size_t XID> friend class CountingAllocator;
276 AllocController *P;
277};
278
279
280template <size_t ID>
281class CountingAllocator<void, ID>
282{
283public:
284 typedef void* pointer;
285 typedef const void* const_pointer;
286 typedef void value_type;
287
288 template <class U>
289 struct rebind { using other = CountingAllocator<U, ID>; };
290
291 CountingAllocator() = delete;
292 explicit CountingAllocator(AllocController& PP) : P(&PP) {}
293
294 CountingAllocator(CountingAllocator const& other) : P(other.P) {
295 P->copy_constructed += 1;
296 }
297
298 CountingAllocator(CountingAllocator&& other) : P(other.P) {
299 P->move_constructed += 1;
300 }
301
302 template <class U>
303 CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) {
304 P->copy_constructed += 1;
305 }
306
307 template <class U>
308 CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) {
309 P->move_constructed += 1;
310 }
311
312 void construct(...) = delete;
313 void destroy(void*) = delete;
314
315 AllocController& getController() const { return *P; }
316
317private:
318 template <class Tp, size_t> friend class CountingAllocator;
319 AllocController *P;
320};
321
322template <class T, class U, size_t ID>
323inline bool operator==(CountingAllocator<T, ID> const& x,
324 CountingAllocator<U, ID> const& y) {
325 return &x.getController() == &y.getController();
326}
327
328template <class T, class U, size_t ID>
329inline bool operator!=(CountingAllocator<T, ID> const& x,
330 CountingAllocator<U, ID> const& y) {
331 return !(x == y);
332}
333
334template <class T>
335class MinAlignedAllocator
336{
337public:
338 typedef T value_type;
339 typedef T* pointer;
340
341 MinAlignedAllocator() = delete;
342
343 explicit MinAlignedAllocator(AllocController& R) : P(&R) {}
344
345 MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) {
346 P->copy_constructed += 1;
347 }
348
349 MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) {
350 P->move_constructed += 1;
351 }
352
353 template <class U>
354 MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
355 P->copy_constructed += 1;
356 }
357
358 template <class U>
359 MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
360 P->move_constructed += 1;
361 }
362
363 T* allocate(std::size_t n) {
364 char* aligned_ptr = (char*)::operator new(alloc_size(n*sizeof(T)));
365 assert(is_max_aligned(aligned_ptr));
366
367 char* unaligned_ptr = aligned_ptr + alignof(T);
368 assert(is_min_aligned(unaligned_ptr));
369
370 P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T));
371
372 return ((T*)unaligned_ptr);
373 }
374
375 void deallocate(T* p, std::size_t n) {
376 assert(is_min_aligned(p));
377
378 char* aligned_ptr = ((char*)p) - alignof(T);
379 assert(is_max_aligned(aligned_ptr));
380
381 P->countDealloc(p, n*sizeof(T), alignof(T));
382
383 return ::operator delete(static_cast<void*>(aligned_ptr));
384 }
385
386 template <class U, class ...Args>
387 void construct(U *p, Args&&... args) {
388 auto *c = ::new ((void*)p) U(std::forward<Args>(args)...);
389 P->countConstruct<Args&&...>(*this, p);
390 }
391
392 template <class U>
393 void destroy(U* p) {
394 p->~U();
395 P->countDestroy(*this, p);
396 }
397
398 AllocController& getController() const { return *P; }
399
400private:
401 static const std::size_t BlockSize = alignof(std::max_align_t);
402
403 static std::size_t alloc_size(std::size_t s) {
404 std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1);
405 bytes += BlockSize;
406 assert(bytes % BlockSize == 0);
407 return bytes;
408 }
409
410 static bool is_max_aligned(void* p) {
411 return reinterpret_cast<std::uintptr_t>(p) % BlockSize == 0;
412 }
413
414 static bool is_min_aligned(void* p) {
415 if (alignof(T) == BlockSize) {
416 return is_max_aligned(p);
417 } else {
418 return reinterpret_cast<std::uintptr_t>(p) % BlockSize == alignof(T);
419 }
420 }
421
422 template <class Tp> friend class MinAlignedAllocator;
423 mutable AllocController *P;
424};
425
426
427template <class T, class U>
428inline bool operator==(MinAlignedAllocator<T> const& x,
429 MinAlignedAllocator<U> const& y) {
430 return &x.getController() == &y.getController();
431}
432
433template <class T, class U>
434inline bool operator!=(MinAlignedAllocator<T> const& x,
435 MinAlignedAllocator<U> const& y) {
436 return !(x == y);
437}
438
439template <class T>
440class NullAllocator
441{
442public:
443 typedef T value_type;
444 typedef T* pointer;
445 NullAllocator() = delete;
446 explicit NullAllocator(AllocController& PP) : P(&PP) {}
447
448 NullAllocator(NullAllocator const& other) : P(other.P) {
449 P->copy_constructed += 1;
450 }
451
452 NullAllocator(NullAllocator&& other) : P(other.P) {
453 P->move_constructed += 1;
454 }
455
456 template <class U>
457 NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
458 P->copy_constructed += 1;
459 }
460
461 template <class U>
462 NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
463 P->move_constructed += 1;
464 }
465
466 T* allocate(std::size_t n)
467 {
468 P->countAlloc(nullptr, n*sizeof(T), alignof(T));
469 return nullptr;
470 }
471
472 void deallocate(T* p, std::size_t n)
473 {
474 void* vp = static_cast<void*>(p);
475 P->countDealloc(vp, n*sizeof(T), alignof(T));
476 }
477
478 AllocController& getController() const { return *P; }
479
480private:
481 template <class Tp> friend class NullAllocator;
482 AllocController *P;
483};
484
485template <class T, class U>
486inline bool operator==(NullAllocator<T> const& x,
487 NullAllocator<U> const& y) {
488 return &x.getController() == &y.getController();
489}
490
491template <class T, class U>
492inline bool operator!=(NullAllocator<T> const& x,
493 NullAllocator<U> const& y) {
494 return !(x == y);
495}
496
497
498#endif /* SUPPORT_CONTROLLED_ALLOCATORS_HPP */