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 alloc_max_size(Alloc const &a) {
26   typedef std::allocator_traits<Alloc> AT;
27   return AT::max_size(a);
28 }
29 
30 class test_alloc_base
31 {
32 protected:
33     static int time_to_throw;
34 public:
35     static int throw_after;
36     static int count;
37     static int alloc_count;
38     static int copied;
39     static int moved;
40     static int converted;
41 
42     const static int destructed_value = -1;
43     const static int default_value = 0;
44     const static int moved_value = INT_MAX;
45 
clear()46     static void clear() {
47       assert(count == 0 && "clearing leaking allocator data?");
48       count = 0;
49       time_to_throw = 0;
50       alloc_count = 0;
51       throw_after = INT_MAX;
52       clear_ctor_counters();
53     }
54 
clear_ctor_counters()55     static void clear_ctor_counters() {
56       copied = 0;
57       moved = 0;
58       converted = 0;
59     }
60 };
61 
62 int test_alloc_base::count = 0;
63 int test_alloc_base::time_to_throw = 0;
64 int test_alloc_base::alloc_count = 0;
65 int test_alloc_base::throw_after = INT_MAX;
66 int test_alloc_base::copied = 0;
67 int test_alloc_base::moved = 0;
68 int test_alloc_base::converted = 0;
69 
70 template <class T>
71 class test_allocator
72     : public test_alloc_base
73 {
74     int data_; // participates in equality
75     int id_; // unique identifier, doesn't participate in equality
76     template <class U> friend class test_allocator;
77 public:
78 
79     typedef unsigned                                                   size_type;
80     typedef int                                                        difference_type;
81     typedef T                                                          value_type;
82     typedef value_type*                                                pointer;
83     typedef const value_type*                                          const_pointer;
84     typedef typename std::add_lvalue_reference<value_type>::type       reference;
85     typedef typename std::add_lvalue_reference<const value_type>::type const_reference;
86 
87     template <class U> struct rebind {typedef test_allocator<U> other;};
88 
test_allocator()89     test_allocator() TEST_NOEXCEPT : data_(0), id_(0) {++count;}
data_(i)90     explicit test_allocator(int i, int id = 0) TEST_NOEXCEPT : data_(i), id_(id)
91       {++count;}
test_allocator(const test_allocator & a)92     test_allocator(const test_allocator& a) TEST_NOEXCEPT : data_(a.data_),
93                                                             id_(a.id_) {
94       ++count;
95       ++copied;
96       assert(a.data_ != destructed_value && a.id_ != destructed_value &&
97              "copying from destroyed allocator");
98     }
99 #if TEST_STD_VER >= 11
test_allocator(test_allocator && a)100     test_allocator(test_allocator&& a) TEST_NOEXCEPT : data_(a.data_),
101                                                        id_(a.id_) {
102       ++count;
103       ++moved;
104       assert(a.data_ != destructed_value && a.id_ != destructed_value &&
105              "moving from destroyed allocator");
106       a.data_ = moved_value;
107       a.id_ = moved_value;
108     }
109 #endif
110     template <class U>
test_allocator(const test_allocator<U> & a)111     test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT : data_(a.data_),
112                                                                id_(a.id_) {
113       ++count;
114       ++converted;
115     }
~test_allocator()116     ~test_allocator() TEST_NOEXCEPT {
117       assert(data_ >= 0); assert(id_ >= 0);
118       --count;
119       data_ = destructed_value;
120       id_ = destructed_value;
121     }
address(reference x)122     pointer address(reference x) const {return &x;}
address(const_reference x)123     const_pointer address(const_reference x) const {return &x;}
124     pointer allocate(size_type n, const void* = 0)
125         {
126             assert(data_ >= 0);
127             if (time_to_throw >= throw_after) {
128 #ifndef TEST_HAS_NO_EXCEPTIONS
129                 throw std::bad_alloc();
130 #else
131                 std::terminate();
132 #endif
133             }
134             ++time_to_throw;
135             ++alloc_count;
136             return (pointer)::operator new(n * sizeof(T));
137         }
deallocate(pointer p,size_type)138     void deallocate(pointer p, size_type)
139         {assert(data_ >= 0); --alloc_count; ::operator delete((void*)p);}
max_size()140     size_type max_size() const TEST_NOEXCEPT
141         {return UINT_MAX / sizeof(T);}
142 #if TEST_STD_VER < 11
construct(pointer p,const T & val)143     void construct(pointer p, const T& val)
144         {::new(static_cast<void*>(p)) T(val);}
145 #else
construct(pointer p,U && val)146     template <class U> void construct(pointer p, U&& val)
147         {::new(static_cast<void*>(p)) T(std::forward<U>(val));}
148 #endif
destroy(pointer p)149     void destroy(pointer p)
150         {p->~T();}
151     friend bool operator==(const test_allocator& x, const test_allocator& y)
152         {return x.data_ == y.data_;}
153     friend bool operator!=(const test_allocator& x, const test_allocator& y)
154         {return !(x == y);}
155 
get_data()156     int get_data() const { return data_; }
get_id()157     int get_id() const { return id_; }
158 };
159 
160 template <class T>
161 class non_default_test_allocator
162     : public test_alloc_base
163 {
164     int data_;
165 
166     template <class U> friend class non_default_test_allocator;
167 public:
168 
169     typedef unsigned                                                   size_type;
170     typedef int                                                        difference_type;
171     typedef T                                                          value_type;
172     typedef value_type*                                                pointer;
173     typedef const value_type*                                          const_pointer;
174     typedef typename std::add_lvalue_reference<value_type>::type       reference;
175     typedef typename std::add_lvalue_reference<const value_type>::type const_reference;
176 
177     template <class U> struct rebind {typedef non_default_test_allocator<U> other;};
178 
179 //    non_default_test_allocator() TEST_NOEXCEPT : data_(0) {++count;}
non_default_test_allocator(int i)180     explicit non_default_test_allocator(int i) TEST_NOEXCEPT : data_(i) {++count;}
non_default_test_allocator(const non_default_test_allocator & a)181     non_default_test_allocator(const non_default_test_allocator& a) TEST_NOEXCEPT
182         : data_(a.data_) {++count;}
non_default_test_allocator(const non_default_test_allocator<U> & a)183     template <class U> non_default_test_allocator(const non_default_test_allocator<U>& a) TEST_NOEXCEPT
184         : data_(a.data_) {++count;}
~non_default_test_allocator()185     ~non_default_test_allocator() TEST_NOEXCEPT {assert(data_ >= 0); --count; data_ = -1;}
address(reference x)186     pointer address(reference x) const {return &x;}
address(const_reference x)187     const_pointer address(const_reference x) const {return &x;}
188     pointer allocate(size_type n, const void* = 0)
189         {
190             assert(data_ >= 0);
191             if (time_to_throw >= throw_after) {
192 #ifndef TEST_HAS_NO_EXCEPTIONS
193                 throw std::bad_alloc();
194 #else
195                 std::terminate();
196 #endif
197             }
198             ++time_to_throw;
199             ++alloc_count;
200             return (pointer)::operator new (n * sizeof(T));
201         }
deallocate(pointer p,size_type)202     void deallocate(pointer p, size_type)
203         {assert(data_ >= 0); --alloc_count; ::operator delete((void*)p); }
max_size()204     size_type max_size() const TEST_NOEXCEPT
205         {return UINT_MAX / sizeof(T);}
206 #if TEST_STD_VER < 11
construct(pointer p,const T & val)207     void construct(pointer p, const T& val)
208         {::new(static_cast<void*>(p)) T(val);}
209 #else
construct(pointer p,U && val)210     template <class U> void construct(pointer p, U&& val)
211         {::new(static_cast<void*>(p)) T(std::forward<U>(val));}
212 #endif
destroy(pointer p)213     void destroy(pointer p) {p->~T();}
214 
215     friend bool operator==(const non_default_test_allocator& x, const non_default_test_allocator& y)
216         {return x.data_ == y.data_;}
217     friend bool operator!=(const non_default_test_allocator& x, const non_default_test_allocator& y)
218         {return !(x == y);}
219 };
220 
221 template <>
222 class test_allocator<void>
223     : public test_alloc_base
224 {
225     int data_;
226     int id_;
227 
228     template <class U> friend class test_allocator;
229 public:
230 
231     typedef unsigned                                                   size_type;
232     typedef int                                                        difference_type;
233     typedef void                                                       value_type;
234     typedef value_type*                                                pointer;
235     typedef const value_type*                                          const_pointer;
236 
237     template <class U> struct rebind {typedef test_allocator<U> other;};
238 
test_allocator()239     test_allocator() TEST_NOEXCEPT : data_(0), id_(0) {}
data_(i)240     explicit test_allocator(int i, int id = 0) TEST_NOEXCEPT : data_(i), id_(id) {}
test_allocator(const test_allocator & a)241     test_allocator(const test_allocator& a) TEST_NOEXCEPT
242         : data_(a.data_), id_(a.id_) {}
test_allocator(const test_allocator<U> & a)243     template <class U> test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT
244         : data_(a.data_), id_(a.id_) {}
~test_allocator()245     ~test_allocator() TEST_NOEXCEPT {data_ = -1; id_ = -1; }
246 
get_id()247     int get_id() const { return id_; }
get_data()248     int get_data() const { return data_; }
249 
250     friend bool operator==(const test_allocator& x, const test_allocator& y)
251         {return x.data_ == y.data_;}
252     friend bool operator!=(const test_allocator& x, const test_allocator& y)
253         {return !(x == y);}
254 };
255 
256 template <class T>
257 class other_allocator
258 {
259     int data_;
260 
261     template <class U> friend class other_allocator;
262 
263 public:
264     typedef T value_type;
265 
other_allocator()266     other_allocator() : data_(-1) {}
other_allocator(int i)267     explicit other_allocator(int i) : data_(i) {}
other_allocator(const other_allocator<U> & a)268     template <class U> other_allocator(const other_allocator<U>& a)
269         : data_(a.data_) {}
allocate(std::size_t n)270     T* allocate(std::size_t n)
271         {return (T*)::operator new(n * sizeof(T));}
deallocate(T * p,std::size_t)272     void deallocate(T* p, std::size_t)
273         {::operator delete((void*)p);}
274 
select_on_container_copy_construction()275     other_allocator select_on_container_copy_construction() const
276         {return other_allocator(-2);}
277 
278     friend bool operator==(const other_allocator& x, const other_allocator& y)
279         {return x.data_ == y.data_;}
280     friend bool operator!=(const other_allocator& x, const other_allocator& y)
281         {return !(x == y);}
282 
283     typedef std::true_type propagate_on_container_copy_assignment;
284     typedef std::true_type propagate_on_container_move_assignment;
285     typedef std::true_type propagate_on_container_swap;
286 
287 #if TEST_STD_VER < 11
max_size()288     std::size_t max_size() const
289         {return UINT_MAX / sizeof(T);}
290 #endif
291 
292 };
293 
294 #if TEST_STD_VER >= 11
295 
296 struct Ctor_Tag {};
297 
298 template <typename T> class TaggingAllocator;
299 
300 struct Tag_X {
301   // All constructors must be passed the Tag type.
302 
303   // DefaultInsertable into vector<X, TaggingAllocator<X>>,
Tag_XTag_X304   Tag_X(Ctor_Tag) {}
305   // CopyInsertable into vector<X, TaggingAllocator<X>>,
Tag_XTag_X306   Tag_X(Ctor_Tag, const Tag_X&) {}
307   // MoveInsertable into vector<X, TaggingAllocator<X>>, and
Tag_XTag_X308   Tag_X(Ctor_Tag, Tag_X&&) {}
309 
310   // EmplaceConstructible into vector<X, TaggingAllocator<X>> from args.
311   template<typename... Args>
Tag_XTag_X312   Tag_X(Ctor_Tag, Args&&...) { }
313 
314   // not DefaultConstructible, CopyConstructible or MoveConstructible.
315   Tag_X() = delete;
316   Tag_X(const Tag_X&) = delete;
317   Tag_X(Tag_X&&) = delete;
318 
319   // CopyAssignable.
320   Tag_X& operator=(const Tag_X&) { return *this; }
321 
322   // MoveAssignable.
323   Tag_X& operator=(Tag_X&&) { return *this; }
324 
325 private:
326   // Not Destructible.
~Tag_XTag_X327   ~Tag_X() { }
328 
329   // Erasable from vector<X, TaggingAllocator<X>>.
330   friend class TaggingAllocator<Tag_X>;
331 };
332 
333 
334 template<typename T>
335 class TaggingAllocator {
336 public:
337     using value_type = T;
338     TaggingAllocator() = default;
339 
340     template<typename U>
TaggingAllocator(const TaggingAllocator<U> &)341       TaggingAllocator(const TaggingAllocator<U>&) { }
342 
allocate(std::size_t n)343     T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); }
344 
deallocate(T * p,std::size_t n)345     void deallocate(T* p, std::size_t n) { std::allocator<T>{}.deallocate(p, n); }
346 
347     template<typename... Args>
construct(Tag_X * p,Args &&...args)348     void construct(Tag_X* p, Args&&... args)
349     { ::new((void*)p) Tag_X(Ctor_Tag{}, std::forward<Args>(args)...); }
350 
351     template<typename U, typename... Args>
construct(U * p,Args &&...args)352     void construct(U* p, Args&&... args)
353     { ::new((void*)p) U(std::forward<Args>(args)...); }
354 
355     template<typename U, typename... Args>
destroy(U * p)356     void destroy(U* p)
357     { p->~U(); }
358 };
359 
360 template<typename T, typename U>
361 bool
362 operator==(const TaggingAllocator<T>&, const TaggingAllocator<U>&)
363 { return true; }
364 
365 template<typename T, typename U>
366 bool
367 operator!=(const TaggingAllocator<T>&, const TaggingAllocator<U>&)
368 { return false; }
369 #endif
370 
371 template <std::size_t MaxAllocs>
372 struct limited_alloc_handle {
373   std::size_t outstanding_;
374   void* last_alloc_;
375 
limited_alloc_handlelimited_alloc_handle376   limited_alloc_handle() : outstanding_(0), last_alloc_(nullptr) {}
377 
378   template <class T>
allocatelimited_alloc_handle379   T *allocate(std::size_t N) {
380     if (N + outstanding_ > MaxAllocs)
381       TEST_THROW(std::bad_alloc());
382     last_alloc_ = ::operator new(N*sizeof(T));
383     outstanding_ += N;
384     return static_cast<T*>(last_alloc_);
385   }
386 
deallocatelimited_alloc_handle387   void deallocate(void* ptr, std::size_t N) {
388     if (ptr == last_alloc_) {
389       last_alloc_ = nullptr;
390       assert(outstanding_ >= N);
391       outstanding_ -= N;
392     }
393     ::operator delete(ptr);
394   }
395 };
396 
397 template <class T, std::size_t N>
398 class limited_allocator
399 {
400     template <class U, std::size_t UN> friend class limited_allocator;
401     typedef limited_alloc_handle<N> BuffT;
402     std::shared_ptr<BuffT> handle_;
403 public:
404     typedef T                 value_type;
405     typedef value_type*       pointer;
406     typedef const value_type* const_pointer;
407     typedef value_type&       reference;
408     typedef const value_type& const_reference;
409     typedef std::size_t       size_type;
410     typedef std::ptrdiff_t    difference_type;
411 
412     template <class U> struct rebind { typedef limited_allocator<U, N> other; };
413 
limited_allocator()414     limited_allocator() : handle_(new BuffT) {}
415 
limited_allocator(limited_allocator const & other)416     limited_allocator(limited_allocator const& other) : handle_(other.handle_) {}
417 
418     template <class U>
limited_allocator(limited_allocator<U,N> const & other)419     explicit limited_allocator(limited_allocator<U, N> const& other)
420         : handle_(other.handle_) {}
421 
422 private:
423     limited_allocator& operator=(const limited_allocator&);// = delete;
424 
425 public:
allocate(size_type n)426     pointer allocate(size_type n) { return handle_->template allocate<T>(n); }
deallocate(pointer p,size_type n)427     void deallocate(pointer p, size_type n) { handle_->deallocate(p, n); }
max_size()428     size_type max_size() const {return N;}
429 
getHandle()430     BuffT* getHandle() const { return handle_.get(); }
431 };
432 
433 template <class T, class U, std::size_t N>
434 inline bool operator==(limited_allocator<T, N> const& LHS,
435                        limited_allocator<U, N> const& RHS) {
436   return LHS.getHandle() == RHS.getHandle();
437 }
438 
439 template <class T, class U, std::size_t N>
440 inline bool operator!=(limited_allocator<T, N> const& LHS,
441                        limited_allocator<U, N> const& RHS) {
442   return !(LHS == RHS);
443 }
444 
445 
446 #endif  // TEST_ALLOCATOR_H
447