1 //===----------------------------------------------------------------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef SUPPORT_CONTROLLED_ALLOCATORS_H 10 #define SUPPORT_CONTROLLED_ALLOCATORS_H 11 12 #include <memory> 13 #include <type_traits> 14 #include <cstddef> 15 #include <cstdlib> 16 #include <cstring> 17 #include <cstdint> 18 #include <cassert> 19 #include <new> 20 21 #include "test_macros.h" 22 #include "type_id.h" 23 24 #if TEST_STD_VER < 11 25 #error This header requires C++11 or greater 26 #endif 27 28 struct AllocController; 29 // 'AllocController' is a concrete type that instruments and controls the 30 // behavior of test allocators. 31 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. 36 37 template <class T> 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 43 // pointer. 44 45 template <class T> 46 class NullAllocator; 47 // 'NullAllocator' is an instrumented test type which implements the 48 // 'Allocator' requirements except that 'allocator' and 'deallocate' are 49 // nops. 50 51 52 #define DISALLOW_COPY(Type) \ 53 Type(Type const&) = delete; \ 54 Type& operator=(Type const&) = delete 55 56 constexpr std::size_t MaxAlignV = alignof(std::max_align_t); 57 58 struct TestException {}; 59 60 struct AllocController { 61 int copy_constructed = 0; 62 int move_constructed = 0; 63 64 int alive = 0; 65 int alloc_count = 0; 66 int dealloc_count = 0; 67 int is_equal_count = 0; 68 69 std::size_t alive_size; 70 std::size_t allocated_size; 71 std::size_t deallocated_size; 72 73 std::size_t last_size = 0; 74 std::size_t last_align = 0; 75 void * last_pointer = 0; 76 77 std::size_t last_alloc_size = 0; 78 std::size_t last_alloc_align = 0; 79 void * last_alloc_pointer = nullptr; 80 81 std::size_t last_dealloc_size = 0; 82 std::size_t last_dealloc_align = 0; 83 void * last_dealloc_pointer = nullptr; 84 85 bool throw_on_alloc = false; 86 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; 92 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; 97 98 AllocController() = default; 99 countAllocAllocController100 void countAlloc(void* p, std::size_t s, size_t a) { 101 ++alive; 102 ++alloc_count; 103 alive_size += s; 104 allocated_size += s; 105 last_pointer = last_alloc_pointer = p; 106 last_size = last_alloc_size = s; 107 last_align = last_alloc_align = a; 108 } 109 countDeallocAllocController110 void countDealloc(void* p, std::size_t s, size_t a) { 111 --alive; 112 ++dealloc_count; 113 alive_size -= s; 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; 118 } 119 120 template <class ...Args, class Alloc, class Tp> countConstructAllocController121 void countConstruct(Alloc const&, Tp *p) { 122 ++construct_called; 123 last_construct_pointer = p; 124 last_construct_alloc = &makeTypeID<Alloc>(); 125 last_construct_type = &makeTypeID<Tp>(); 126 last_construct_args = &makeArgumentID<Args...>(); 127 } 128 129 template <class Alloc, class Tp> countDestroyAllocController130 void countDestroy(Alloc const&, Tp *p) { 131 ++destroy_called; 132 last_destroy_alloc = &makeTypeID<Alloc>(); 133 last_destroy_type = &makeTypeID<Tp>(); 134 last_destroy_pointer = p; 135 } 136 resetAllocController137 void reset() { std::memset(this, 0, sizeof(*this)); } resetConstructDestroyAllocController138 void resetConstructDestroy() { 139 construct_called = 0; 140 last_construct_pointer = nullptr; 141 last_construct_alloc = last_construct_args = last_construct_type = nullptr; 142 destroy_called = 0; 143 last_destroy_alloc = nullptr; 144 last_destroy_pointer = nullptr; 145 } 146 public: checkAllocAllocController147 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; 151 } 152 checkAllocAllocController153 bool checkAlloc(void* p, std::size_t s) const { 154 return p == last_alloc_pointer && 155 s == last_alloc_size; 156 } 157 checkAllocAtLeastAllocController158 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; 162 } 163 checkAllocAtLeastAllocController164 bool checkAllocAtLeast(void* p, std::size_t s) const { 165 return p == last_alloc_pointer && 166 s <= last_alloc_size; 167 } 168 checkDeallocAllocController169 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; 173 } 174 checkDeallocAllocController175 bool checkDealloc(void* p, std::size_t s) const { 176 return p == last_dealloc_pointer && 177 s == last_dealloc_size; 178 } 179 checkDeallocMatchesAllocAllocController180 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; 184 } 185 186 template <class ...Args, class Alloc, class Tp> checkConstructAllocController187 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) 192 return false; 193 if (last_construct_alloc != expectAlloc) 194 return false; 195 if (last_construct_type != expectTp) 196 return false; 197 if (last_construct_args != expectArgs) 198 return false; 199 return true; 200 } 201 202 template <class Alloc, class Tp> checkDestroyAllocController203 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>(); 207 } 208 checkDestroyMatchesConstructAllocController209 bool checkDestroyMatchesConstruct() const { 210 return last_destroy_pointer == last_construct_pointer && 211 last_destroy_type == last_construct_type; 212 } 213 countIsEqualAllocController214 void countIsEqual() { 215 ++is_equal_count; 216 } 217 checkIsEqualCalledEqAllocController218 bool checkIsEqualCalledEq(int n) const { 219 return is_equal_count == n; 220 } 221 private: 222 DISALLOW_COPY(AllocController); 223 }; 224 225 template <class T, std::size_t ID> 226 class CountingAllocator 227 { 228 public: 229 typedef T value_type; 230 typedef T* pointer; 231 232 template <class U> 233 struct rebind { using other = CountingAllocator<U, ID>; }; 234 235 CountingAllocator() = delete; CountingAllocator(AllocController & PP)236 explicit CountingAllocator(AllocController& PP) : P(&PP) {} 237 CountingAllocator(CountingAllocator const & other)238 CountingAllocator(CountingAllocator const& other) : P(other.P) { 239 P->copy_constructed += 1; 240 } 241 CountingAllocator(CountingAllocator && other)242 CountingAllocator(CountingAllocator&& other) : P(other.P) { 243 P->move_constructed += 1; 244 } 245 246 template <class U> CountingAllocator(CountingAllocator<U,ID> const & other)247 CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) { 248 P->copy_constructed += 1; 249 } 250 251 template <class U> CountingAllocator(CountingAllocator<U,ID> && other)252 CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) { 253 P->move_constructed += 1; 254 } 255 allocate(std::size_t n)256 T* allocate(std::size_t n) 257 { 258 void* ret = ::operator new(n*sizeof(T)); 259 P->countAlloc(ret, n*sizeof(T), alignof(T)); 260 return static_cast<T*>(ret); 261 } 262 deallocate(T * p,std::size_t n)263 void deallocate(T* p, std::size_t n) 264 { 265 void* vp = static_cast<void*>(p); 266 P->countDealloc(vp, n*sizeof(T), alignof(T)); 267 ::operator delete(vp); 268 } 269 270 template <class U, class ...Args> construct(U * p,Args &&...args)271 void construct(U *p, Args&&... args) { 272 ::new ((void*)p) U(std::forward<Args>(args)...); 273 P->countConstruct<Args&&...>(*this, p); 274 } 275 276 template <class U> destroy(U * p)277 void destroy(U* p) { 278 p->~U(); 279 P->countDestroy(*this, p); 280 } 281 getController()282 AllocController& getController() const { return *P; } 283 284 private: 285 template <class Tp, std::size_t XID> friend class CountingAllocator; 286 AllocController *P; 287 }; 288 289 290 template <std::size_t ID> 291 class CountingAllocator<void, ID> 292 { 293 public: 294 typedef void* pointer; 295 typedef const void* const_pointer; 296 typedef void value_type; 297 298 template <class U> 299 struct rebind { using other = CountingAllocator<U, ID>; }; 300 301 CountingAllocator() = delete; CountingAllocator(AllocController & PP)302 explicit CountingAllocator(AllocController& PP) : P(&PP) {} 303 CountingAllocator(CountingAllocator const & other)304 CountingAllocator(CountingAllocator const& other) : P(other.P) { 305 P->copy_constructed += 1; 306 } 307 CountingAllocator(CountingAllocator && other)308 CountingAllocator(CountingAllocator&& other) : P(other.P) { 309 P->move_constructed += 1; 310 } 311 312 template <class U> CountingAllocator(CountingAllocator<U,ID> const & other)313 CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) { 314 P->copy_constructed += 1; 315 } 316 317 template <class U> CountingAllocator(CountingAllocator<U,ID> && other)318 CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) { 319 P->move_constructed += 1; 320 } 321 322 void construct(...) = delete; 323 void destroy(void*) = delete; 324 getController()325 AllocController& getController() const { return *P; } 326 327 private: 328 template <class Tp, std::size_t> friend class CountingAllocator; 329 AllocController *P; 330 }; 331 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(); 336 } 337 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) { 341 return !(x == y); 342 } 343 344 template <class T> 345 class MinAlignedAllocator 346 { 347 public: 348 typedef T value_type; 349 typedef T* pointer; 350 351 MinAlignedAllocator() = delete; 352 MinAlignedAllocator(AllocController & R)353 explicit MinAlignedAllocator(AllocController& R) : P(&R) {} 354 MinAlignedAllocator(MinAlignedAllocator const & other)355 MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) { 356 P->copy_constructed += 1; 357 } 358 MinAlignedAllocator(MinAlignedAllocator && other)359 MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) { 360 P->move_constructed += 1; 361 } 362 363 template <class U> MinAlignedAllocator(MinAlignedAllocator<U> const & other)364 MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { 365 P->copy_constructed += 1; 366 } 367 368 template <class U> MinAlignedAllocator(MinAlignedAllocator<U> && other)369 MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { 370 P->move_constructed += 1; 371 } 372 allocate(std::size_t n)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)); 376 377 char* unaligned_ptr = aligned_ptr + alignof(T); 378 assert(is_min_aligned(unaligned_ptr)); 379 380 P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T)); 381 382 return ((T*)unaligned_ptr); 383 } 384 deallocate(T * p,std::size_t n)385 void deallocate(T* p, std::size_t n) { 386 assert(is_min_aligned(p)); 387 388 char* aligned_ptr = ((char*)p) - alignof(T); 389 assert(is_max_aligned(aligned_ptr)); 390 391 P->countDealloc(p, n*sizeof(T), alignof(T)); 392 393 return ::operator delete(static_cast<void*>(aligned_ptr)); 394 } 395 396 template <class U, class ...Args> construct(U * p,Args &&...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); 400 } 401 402 template <class U> destroy(U * p)403 void destroy(U* p) { 404 p->~U(); 405 P->countDestroy(*this, p); 406 } 407 getController()408 AllocController& getController() const { return *P; } 409 410 private: 411 static const std::size_t BlockSize = alignof(std::max_align_t); 412 alloc_size(std::size_t s)413 static std::size_t alloc_size(std::size_t s) { 414 std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1); 415 bytes += BlockSize; 416 assert(bytes % BlockSize == 0); 417 return bytes; 418 } 419 is_max_aligned(void * p)420 static bool is_max_aligned(void* p) { 421 return reinterpret_cast<std::uintptr_t>(p) % BlockSize == 0; 422 } 423 is_min_aligned(void * p)424 static bool is_min_aligned(void* p) { 425 if (alignof(T) == BlockSize) { 426 return is_max_aligned(p); 427 } else { 428 return reinterpret_cast<std::uintptr_t>(p) % BlockSize == alignof(T); 429 } 430 } 431 432 template <class Tp> friend class MinAlignedAllocator; 433 mutable AllocController *P; 434 }; 435 436 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(); 441 } 442 443 template <class T, class U> 444 inline bool operator!=(MinAlignedAllocator<T> const& x, 445 MinAlignedAllocator<U> const& y) { 446 return !(x == y); 447 } 448 449 template <class T> 450 class NullAllocator 451 { 452 public: 453 typedef T value_type; 454 typedef T* pointer; 455 NullAllocator() = delete; NullAllocator(AllocController & PP)456 explicit NullAllocator(AllocController& PP) : P(&PP) {} 457 NullAllocator(NullAllocator const & other)458 NullAllocator(NullAllocator const& other) : P(other.P) { 459 P->copy_constructed += 1; 460 } 461 NullAllocator(NullAllocator && other)462 NullAllocator(NullAllocator&& other) : P(other.P) { 463 P->move_constructed += 1; 464 } 465 466 template <class U> NullAllocator(NullAllocator<U> const & other)467 NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { 468 P->copy_constructed += 1; 469 } 470 471 template <class U> NullAllocator(NullAllocator<U> && other)472 NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { 473 P->move_constructed += 1; 474 } 475 allocate(std::size_t n)476 T* allocate(std::size_t n) 477 { 478 P->countAlloc(nullptr, n*sizeof(T), alignof(T)); 479 return nullptr; 480 } 481 deallocate(T * p,std::size_t n)482 void deallocate(T* p, std::size_t n) 483 { 484 void* vp = static_cast<void*>(p); 485 P->countDealloc(vp, n*sizeof(T), alignof(T)); 486 } 487 getController()488 AllocController& getController() const { return *P; } 489 490 private: 491 template <class Tp> friend class NullAllocator; 492 AllocController *P; 493 }; 494 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(); 499 } 500 501 template <class T, class U> 502 inline bool operator!=(NullAllocator<T> const& x, 503 NullAllocator<U> const& y) { 504 return !(x == y); 505 } 506 507 508 #endif /* SUPPORT_CONTROLLED_ALLOCATORS_H */ 509