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 TEST_ALLOCATOR_H 10 #define TEST_ALLOCATOR_H 11 12 #include <type_traits> 13 #include <new> 14 #include <memory> 15 #include <utility> 16 #include <cstddef> 17 #include <cstdlib> 18 #include <climits> 19 #include <cassert> 20 21 #include "test_macros.h" 22 23 template <class Alloc> 24 inline typename std::allocator_traits<Alloc>::size_type alloc_max_size(Alloc const& a) { 25 typedef std::allocator_traits<Alloc> AT; 26 return AT::max_size(a); 27 } 28 29 struct test_allocator_statistics { 30 int time_to_throw = 0; 31 int throw_after = INT_MAX; 32 int count = 0; 33 int alloc_count = 0; 34 int copied = 0; 35 int moved = 0; 36 int converted = 0; 37 38 TEST_CONSTEXPR_CXX14 void clear() { 39 assert(count == 0 && "clearing leaking allocator data?"); 40 count = 0; 41 time_to_throw = 0; 42 alloc_count = 0; 43 throw_after = INT_MAX; 44 clear_ctor_counters(); 45 } 46 47 TEST_CONSTEXPR_CXX14 void clear_ctor_counters() { 48 copied = 0; 49 moved = 0; 50 converted = 0; 51 } 52 }; 53 54 struct test_alloc_base { 55 TEST_CONSTEXPR static const int destructed_value = -1; 56 TEST_CONSTEXPR static const int moved_value = INT_MAX; 57 }; 58 59 template <class T> 60 class test_allocator { 61 int data_ = 0; // participates in equality 62 int id_ = 0; // unique identifier, doesn't participate in equality 63 test_allocator_statistics* stats_ = nullptr; 64 65 template <class U> 66 friend class test_allocator; 67 68 public: 69 typedef unsigned size_type; 70 typedef int difference_type; 71 typedef T value_type; 72 typedef value_type* pointer; 73 typedef const value_type* const_pointer; 74 typedef typename std::add_lvalue_reference<value_type>::type reference; 75 typedef typename std::add_lvalue_reference<const value_type>::type const_reference; 76 77 template <class U> 78 struct rebind { 79 typedef test_allocator<U> other; 80 }; 81 82 TEST_CONSTEXPR test_allocator() TEST_NOEXCEPT = default; 83 84 TEST_CONSTEXPR_CXX14 explicit test_allocator(test_allocator_statistics* stats) TEST_NOEXCEPT : stats_(stats) { 85 if (stats_ != nullptr) 86 ++stats_->count; 87 } 88 89 TEST_CONSTEXPR explicit test_allocator(int data) TEST_NOEXCEPT : data_(data) {} 90 91 TEST_CONSTEXPR_CXX14 explicit test_allocator(int data, test_allocator_statistics* stats) TEST_NOEXCEPT 92 : data_(data), stats_(stats) { 93 if (stats != nullptr) 94 ++stats_->count; 95 } 96 97 TEST_CONSTEXPR explicit test_allocator(int data, int id) TEST_NOEXCEPT : data_(data), id_(id) {} 98 99 TEST_CONSTEXPR_CXX14 explicit test_allocator(int data, int id, test_allocator_statistics* stats) TEST_NOEXCEPT 100 : data_(data), id_(id), stats_(stats) { 101 if (stats_ != nullptr) 102 ++stats_->count; 103 } 104 105 TEST_CONSTEXPR_CXX14 test_allocator(const test_allocator& a) TEST_NOEXCEPT 106 : data_(a.data_), id_(a.id_), stats_(a.stats_) { 107 assert(a.data_ != test_alloc_base::destructed_value && a.id_ != test_alloc_base::destructed_value && 108 "copying from destroyed allocator"); 109 if (stats_ != nullptr) { 110 ++stats_->count; 111 ++stats_->copied; 112 } 113 } 114 115 TEST_CONSTEXPR_CXX14 test_allocator(test_allocator&& a) TEST_NOEXCEPT : data_(a.data_), id_(a.id_), stats_(a.stats_) { 116 if (stats_ != nullptr) { 117 ++stats_->count; 118 ++stats_->moved; 119 } 120 assert(a.data_ != test_alloc_base::destructed_value && a.id_ != test_alloc_base::destructed_value && 121 "moving from destroyed allocator"); 122 a.data_ = test_alloc_base::moved_value; 123 a.id_ = test_alloc_base::moved_value; 124 } 125 126 template <class U> 127 TEST_CONSTEXPR_CXX14 test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT 128 : data_(a.data_), id_(a.id_), stats_(a.stats_) { 129 if (stats_ != nullptr) { 130 ++stats_->count; 131 ++stats_->converted; 132 } 133 } 134 135 TEST_CONSTEXPR_CXX20 ~test_allocator() TEST_NOEXCEPT { 136 assert(data_ != test_alloc_base::destructed_value); 137 assert(id_ != test_alloc_base::destructed_value); 138 if (stats_ != nullptr) 139 --stats_->count; 140 data_ = test_alloc_base::destructed_value; 141 id_ = test_alloc_base::destructed_value; 142 } 143 144 TEST_CONSTEXPR pointer address(reference x) const { return &x; } 145 TEST_CONSTEXPR const_pointer address(const_reference x) const { return &x; } 146 147 TEST_CONSTEXPR_CXX14 pointer allocate(size_type n, const void* = 0) { 148 assert(data_ != test_alloc_base::destructed_value); 149 if (stats_ != nullptr) { 150 if (stats_->time_to_throw >= stats_->throw_after) 151 TEST_THROW(std::bad_alloc()); 152 ++stats_->time_to_throw; 153 ++stats_->alloc_count; 154 } 155 return std::allocator<value_type>().allocate(n); 156 } 157 158 TEST_CONSTEXPR_CXX14 void deallocate(pointer p, size_type s) { 159 assert(data_ != test_alloc_base::destructed_value); 160 if (stats_ != nullptr) 161 --stats_->alloc_count; 162 std::allocator<value_type>().deallocate(p, s); 163 } 164 165 TEST_CONSTEXPR size_type max_size() const TEST_NOEXCEPT { return UINT_MAX / sizeof(T); } 166 167 template <class U> 168 TEST_CONSTEXPR_CXX14 void construct(pointer p, U&& val) { 169 ::new (static_cast<void*>(p)) T(std::forward<U>(val)); 170 } 171 172 TEST_CONSTEXPR_CXX14 void destroy(pointer p) { p->~T(); } 173 TEST_CONSTEXPR friend bool operator==(const test_allocator& x, const test_allocator& y) { return x.data_ == y.data_; } 174 TEST_CONSTEXPR friend bool operator!=(const test_allocator& x, const test_allocator& y) { return !(x == y); } 175 176 TEST_CONSTEXPR int get_data() const { return data_; } 177 TEST_CONSTEXPR int get_id() const { return id_; } 178 }; 179 180 template <class T> 181 class non_default_test_allocator { 182 int data_ = 0; 183 test_allocator_statistics* stats_ = nullptr; 184 185 template <class U> 186 friend class non_default_test_allocator; 187 188 public: 189 typedef unsigned size_type; 190 typedef int difference_type; 191 typedef T value_type; 192 typedef value_type* pointer; 193 typedef const value_type* const_pointer; 194 typedef typename std::add_lvalue_reference<value_type>::type reference; 195 typedef typename std::add_lvalue_reference<const value_type>::type const_reference; 196 197 template <class U> 198 struct rebind { 199 typedef non_default_test_allocator<U> other; 200 }; 201 202 TEST_CONSTEXPR_CXX14 203 explicit non_default_test_allocator(int i, test_allocator_statistics* stats = nullptr) TEST_NOEXCEPT 204 : data_(i), stats_(stats) { 205 if (stats_ != nullptr) { 206 ++stats_->count; 207 } 208 } 209 210 TEST_CONSTEXPR_CXX14 211 non_default_test_allocator(const non_default_test_allocator& a) TEST_NOEXCEPT : data_(a.data_), stats_(a.stats_) { 212 if (stats_ != nullptr) 213 ++stats_->count; 214 } 215 216 template <class U> 217 TEST_CONSTEXPR_CXX14 non_default_test_allocator(const non_default_test_allocator<U>& a) TEST_NOEXCEPT 218 : data_(a.data_), stats_(a.stats_) { 219 if (stats_ != nullptr) 220 ++stats_->count; 221 } 222 223 TEST_CONSTEXPR_CXX20 ~non_default_test_allocator() TEST_NOEXCEPT { 224 assert(data_ != test_alloc_base::destructed_value); 225 if (stats_ != nullptr) 226 --stats_->count; 227 data_ = test_alloc_base::destructed_value; 228 } 229 230 TEST_CONSTEXPR pointer address(reference x) const { return &x; } 231 TEST_CONSTEXPR const_pointer address(const_reference x) const { return &x; } 232 233 TEST_CONSTEXPR_CXX20 pointer allocate(size_type n, const void* = nullptr) { 234 assert(data_ != test_alloc_base::destructed_value); 235 if (stats_ != nullptr) { 236 if (stats_->time_to_throw >= stats_->throw_after) 237 TEST_THROW(std::bad_alloc()); 238 ++stats_->time_to_throw; 239 ++stats_->alloc_count; 240 } 241 return std::allocator<value_type>().allocate(n); 242 } 243 244 TEST_CONSTEXPR_CXX20 void deallocate(pointer p, size_type n) { 245 assert(data_ != test_alloc_base::destructed_value); 246 if (stats_ != nullptr) 247 --stats_->alloc_count; 248 std::allocator<value_type>().deallocate(p, n); 249 } 250 251 TEST_CONSTEXPR size_type max_size() const TEST_NOEXCEPT { return UINT_MAX / sizeof(T); } 252 253 TEST_CONSTEXPR friend bool operator==(const non_default_test_allocator& x, const non_default_test_allocator& y) { 254 return x.data_ == y.data_; 255 } 256 257 TEST_CONSTEXPR friend bool operator!=(const non_default_test_allocator& x, const non_default_test_allocator& y) { 258 return !(x == y); 259 } 260 }; 261 262 template <> 263 class test_allocator<void> { 264 int data_ = 0; 265 int id_ = 0; 266 test_allocator_statistics* stats_ = nullptr; 267 268 template <class U> 269 friend class test_allocator; 270 271 public: 272 typedef unsigned size_type; 273 typedef int difference_type; 274 typedef void value_type; 275 typedef value_type* pointer; 276 typedef const value_type* const_pointer; 277 278 template <class U> 279 struct rebind { 280 typedef test_allocator<U> other; 281 }; 282 283 TEST_CONSTEXPR test_allocator() TEST_NOEXCEPT = default; 284 285 TEST_CONSTEXPR_CXX14 explicit test_allocator(test_allocator_statistics* stats) TEST_NOEXCEPT : stats_(stats) {} 286 287 TEST_CONSTEXPR explicit test_allocator(int data) TEST_NOEXCEPT : data_(data) {} 288 289 TEST_CONSTEXPR explicit test_allocator(int data, test_allocator_statistics* stats) TEST_NOEXCEPT 290 : data_(data), stats_(stats) 291 {} 292 293 TEST_CONSTEXPR explicit test_allocator(int data, int id) : data_(data), id_(id) {} 294 295 TEST_CONSTEXPR_CXX14 explicit test_allocator(int data, int id, test_allocator_statistics* stats) TEST_NOEXCEPT 296 : data_(data), id_(id), stats_(stats) 297 {} 298 299 TEST_CONSTEXPR_CXX14 explicit test_allocator(const test_allocator& a) TEST_NOEXCEPT 300 : data_(a.data_), id_(a.id_), stats_(a.stats_) 301 {} 302 303 template <class U> 304 TEST_CONSTEXPR_CXX14 test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT 305 : data_(a.data_), id_(a.id_), stats_(a.stats_) 306 {} 307 308 TEST_CONSTEXPR_CXX20 ~test_allocator() TEST_NOEXCEPT { 309 data_ = test_alloc_base::destructed_value; 310 id_ = test_alloc_base::destructed_value; 311 } 312 313 TEST_CONSTEXPR int get_id() const { return id_; } 314 TEST_CONSTEXPR int get_data() const { return data_; } 315 316 TEST_CONSTEXPR friend bool operator==(const test_allocator& x, const test_allocator& y) { return x.data_ == y.data_; } 317 TEST_CONSTEXPR friend bool operator!=(const test_allocator& x, const test_allocator& y) { return !(x == y); } 318 }; 319 320 template <class T> 321 class other_allocator { 322 int data_ = -1; 323 324 template <class U> 325 friend class other_allocator; 326 327 public: 328 typedef T value_type; 329 330 TEST_CONSTEXPR_CXX14 other_allocator() {} 331 TEST_CONSTEXPR_CXX14 explicit other_allocator(int i) : data_(i) {} 332 333 template <class U> 334 TEST_CONSTEXPR_CXX14 other_allocator(const other_allocator<U>& a) : data_(a.data_) {} 335 336 TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<value_type>().allocate(n); } 337 TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t s) { std::allocator<value_type>().deallocate(p, s); } 338 339 TEST_CONSTEXPR_CXX14 other_allocator select_on_container_copy_construction() const { return other_allocator(-2); } 340 341 TEST_CONSTEXPR_CXX14 friend bool operator==(const other_allocator& x, const other_allocator& y) { 342 return x.data_ == y.data_; 343 } 344 345 TEST_CONSTEXPR_CXX14 friend bool operator!=(const other_allocator& x, const other_allocator& y) { return !(x == y); } 346 347 typedef std::true_type propagate_on_container_copy_assignment; 348 typedef std::true_type propagate_on_container_move_assignment; 349 typedef std::true_type propagate_on_container_swap; 350 351 #if TEST_STD_VER < 11 352 std::size_t max_size() const { return UINT_MAX / sizeof(T); } 353 #endif 354 }; 355 356 struct Ctor_Tag {}; 357 358 template <typename T> 359 class TaggingAllocator; 360 361 struct Tag_X { 362 // All constructors must be passed the Tag type. 363 364 // DefaultInsertable into vector<X, TaggingAllocator<X>>, 365 TEST_CONSTEXPR Tag_X(Ctor_Tag) {} 366 // CopyInsertable into vector<X, TaggingAllocator<X>>, 367 TEST_CONSTEXPR Tag_X(Ctor_Tag, const Tag_X&) {} 368 // MoveInsertable into vector<X, TaggingAllocator<X>>, and 369 TEST_CONSTEXPR Tag_X(Ctor_Tag, Tag_X&&) {} 370 371 // EmplaceConstructible into vector<X, TaggingAllocator<X>> from args. 372 template <typename... Args> 373 TEST_CONSTEXPR Tag_X(Ctor_Tag, Args&&...) {} 374 375 // not DefaultConstructible, CopyConstructible or MoveConstructible. 376 Tag_X() = delete; 377 Tag_X(const Tag_X&) = delete; 378 Tag_X(Tag_X&&) = delete; 379 380 // CopyAssignable. 381 TEST_CONSTEXPR_CXX14 Tag_X& operator=(const Tag_X&) { return *this; }; 382 383 // MoveAssignable. 384 TEST_CONSTEXPR_CXX14 Tag_X& operator=(Tag_X&&) { return *this; }; 385 386 private: 387 ~Tag_X() = default; 388 // Erasable from vector<X, TaggingAllocator<X>>. 389 friend class TaggingAllocator<Tag_X>; 390 }; 391 392 template <typename T> 393 class TaggingAllocator { 394 public: 395 using value_type = T; 396 TaggingAllocator() = default; 397 398 template <typename U> 399 TEST_CONSTEXPR TaggingAllocator(const TaggingAllocator<U>&) {} 400 401 template <typename... Args> 402 void construct(Tag_X* p, Args&&... args) { 403 ::new ((void*)p) Tag_X(Ctor_Tag(), std::forward<Args>(args)...); 404 } 405 406 template <typename U> 407 void destroy(U* p) { 408 p->~U(); 409 } 410 411 TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); } 412 TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); } 413 }; 414 415 template <std::size_t MaxAllocs> 416 struct limited_alloc_handle { 417 std::size_t outstanding_ = 0; 418 void* last_alloc_ = nullptr; 419 420 template <class T> 421 TEST_CONSTEXPR_CXX20 T* allocate(std::size_t N) { 422 if (N + outstanding_ > MaxAllocs) 423 TEST_THROW(std::bad_alloc()); 424 last_alloc_ = std::allocator<T>().allocate(N); 425 outstanding_ += N; 426 return static_cast<T*>(last_alloc_); 427 } 428 429 template <class T> 430 TEST_CONSTEXPR_CXX20 void deallocate(T* ptr, std::size_t N) { 431 if (ptr == last_alloc_) { 432 last_alloc_ = nullptr; 433 assert(outstanding_ >= N); 434 outstanding_ -= N; 435 } 436 std::allocator<T>().deallocate(ptr, N); 437 } 438 }; 439 440 namespace detail { 441 template <class T> 442 class thread_unsafe_shared_ptr { 443 public: 444 thread_unsafe_shared_ptr() = default; 445 446 TEST_CONSTEXPR_CXX14 thread_unsafe_shared_ptr(const thread_unsafe_shared_ptr& other) : block(other.block) { 447 ++block->ref_count; 448 } 449 450 TEST_CONSTEXPR_CXX20 ~thread_unsafe_shared_ptr() { 451 --block->ref_count; 452 if (block->ref_count != 0) 453 return; 454 typedef std::allocator_traits<std::allocator<control_block> > allocator_traits; 455 std::allocator<control_block> alloc; 456 allocator_traits::destroy(alloc, block); 457 allocator_traits::deallocate(alloc, block, 1); 458 } 459 460 TEST_CONSTEXPR const T& operator*() const { return block->content; } 461 TEST_CONSTEXPR const T* operator->() const { return &block->content; } 462 TEST_CONSTEXPR_CXX14 T& operator*() { return block->content; } 463 TEST_CONSTEXPR_CXX14 T* operator->() { return &block->content; } 464 TEST_CONSTEXPR_CXX14 T* get() { return &block->content; } 465 TEST_CONSTEXPR const T* get() const { return &block->content; } 466 467 private: 468 struct control_block { 469 template <class... Args> 470 TEST_CONSTEXPR control_block(Args... args) : content(std::forward<Args>(args)...) {} 471 size_t ref_count = 1; 472 T content; 473 }; 474 475 control_block* block = nullptr; 476 477 template <class U, class... Args> 478 friend TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr<U> make_thread_unsafe_shared(Args...); 479 }; 480 481 template <class T, class... Args> 482 TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr<T> make_thread_unsafe_shared(Args... args) { 483 typedef typename thread_unsafe_shared_ptr<T>::control_block control_block_type; 484 typedef std::allocator_traits<std::allocator<control_block_type> > allocator_traits; 485 486 thread_unsafe_shared_ptr<T> ptr; 487 std::allocator<control_block_type> alloc; 488 ptr.block = allocator_traits::allocate(alloc, 1); 489 allocator_traits::construct(alloc, ptr.block, std::forward<Args>(args)...); 490 491 return ptr; 492 } 493 } // namespace detail 494 495 template <class T, std::size_t N> 496 class limited_allocator { 497 template <class U, std::size_t UN> 498 friend class limited_allocator; 499 typedef limited_alloc_handle<N> BuffT; 500 detail::thread_unsafe_shared_ptr<BuffT> handle_; 501 502 public: 503 typedef T value_type; 504 typedef value_type* pointer; 505 typedef const value_type* const_pointer; 506 typedef value_type& reference; 507 typedef const value_type& const_reference; 508 typedef std::size_t size_type; 509 typedef std::ptrdiff_t difference_type; 510 511 template <class U> 512 struct rebind { 513 typedef limited_allocator<U, N> other; 514 }; 515 516 TEST_CONSTEXPR_CXX20 limited_allocator() : handle_(detail::make_thread_unsafe_shared<BuffT>()) {} 517 518 limited_allocator(limited_allocator const&) = default; 519 520 template <class U> 521 TEST_CONSTEXPR explicit limited_allocator(limited_allocator<U, N> const& other) : handle_(other.handle_) {} 522 523 limited_allocator& operator=(const limited_allocator&) = delete; 524 525 TEST_CONSTEXPR_CXX20 pointer allocate(size_type n) { return handle_->template allocate<T>(n); } 526 TEST_CONSTEXPR_CXX20 void deallocate(pointer p, size_type n) { handle_->template deallocate<T>(p, n); } 527 TEST_CONSTEXPR size_type max_size() const { return N; } 528 TEST_CONSTEXPR BuffT* getHandle() const { return handle_.get(); } 529 }; 530 531 template <class T, class U, std::size_t N> 532 TEST_CONSTEXPR inline bool operator==(limited_allocator<T, N> const& LHS, limited_allocator<U, N> const& RHS) { 533 return LHS.getHandle() == RHS.getHandle(); 534 } 535 536 template <class T, class U, std::size_t N> 537 TEST_CONSTEXPR inline bool operator!=(limited_allocator<T, N> const& LHS, limited_allocator<U, N> const& RHS) { 538 return !(LHS == RHS); 539 } 540 541 #endif // TEST_ALLOCATOR_H 542