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