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