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