]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/testsuite/util/testsuite_allocator.h
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / testsuite / util / testsuite_allocator.h
1 // -*- C++ -*-
2 // Testing allocator for the C++ library testsuite.
3 //
4 // Copyright (C) 2002-2024 Free Software Foundation, Inc.
5 //
6 // This file is part of the GNU ISO C++ Library. This library is free
7 // software; you can redistribute it and/or modify it under the
8 // terms of the GNU General Public License as published by the
9 // Free Software Foundation; either version 3, or (at your option)
10 // any later version.
11 //
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this library; see the file COPYING3. If not see
19 // <http://www.gnu.org/licenses/>.
20 //
21
22 // This file provides an test instrumentation allocator that can be
23 // used to verify allocation functionality of standard library
24 // containers. 2002.11.25 smw
25
26 #ifndef _GLIBCXX_TESTSUITE_ALLOCATOR_H
27 #define _GLIBCXX_TESTSUITE_ALLOCATOR_H
28
29 #include <bits/move.h>
30 #include <ext/pointer.h>
31 #include <ext/alloc_traits.h>
32 #include <testsuite_hooks.h>
33 #if __cplusplus >= 201703L
34 # include <memory_resource>
35 # include <new>
36 #endif
37
38 #if __cplusplus >= 201103L
39 # include <unordered_map>
40 namespace unord = std;
41 #else
42 # include <tr1/unordered_map>
43 namespace unord = std::tr1;
44 #endif
45
46 namespace __gnu_test
47 {
48 // A common API for calling max_size() on an allocator in any -std mode.
49 template<typename A>
50 typename A::size_type
51 max_size(const A& a)
52 {
53 #if __cplusplus >= 201103L
54 return std::allocator_traits<A>::max_size(a);
55 #else
56 return a.max_size();
57 #endif
58 }
59
60 class tracker_allocator_counter
61 {
62 public:
63 typedef std::size_t size_type;
64
65 static void
66 allocate(size_type blocksize)
67 { allocationCount_ += blocksize; }
68
69 static void
70 construct() { ++constructCount_; }
71
72 static void
73 destroy() { ++destructCount_; }
74
75 static void
76 deallocate(size_type blocksize)
77 { deallocationCount_ += blocksize; }
78
79 static size_type
80 get_allocation_count() { return allocationCount_; }
81
82 static size_type
83 get_deallocation_count() { return deallocationCount_; }
84
85 static int
86 get_construct_count() { return constructCount_; }
87
88 static int
89 get_destruct_count() { return destructCount_; }
90
91 static void
92 reset()
93 {
94 allocationCount_ = 0;
95 deallocationCount_ = 0;
96 constructCount_ = 0;
97 destructCount_ = 0;
98 }
99
100 private:
101 static size_type allocationCount_;
102 static size_type deallocationCount_;
103 static int constructCount_;
104 static int destructCount_;
105 };
106
107 // Helper to detect inconsistency between type used to instantiate an
108 // allocator and the underlying allocator value_type.
109 template<typename T, typename Alloc,
110 typename = typename Alloc::value_type>
111 struct check_consistent_alloc_value_type;
112
113 template<typename T, typename Alloc>
114 struct check_consistent_alloc_value_type<T, Alloc, T>
115 { typedef T value_type; };
116
117 // An allocator facade that intercepts allocate/deallocate/construct/destroy
118 // calls and track them through the tracker_allocator_counter class. This
119 // class is templated on the target object type, but tracker isn't.
120 template<typename T, typename Alloc = std::allocator<T> >
121 class tracker_allocator : public Alloc
122 {
123 private:
124 typedef tracker_allocator_counter counter_type;
125
126 typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits;
127
128 public:
129 typedef typename
130 check_consistent_alloc_value_type<T, Alloc>::value_type value_type;
131 typedef typename AllocTraits::pointer pointer;
132 typedef typename AllocTraits::size_type size_type;
133
134 template<class U>
135 struct rebind
136 {
137 typedef tracker_allocator<U,
138 typename AllocTraits::template rebind<U>::other> other;
139 };
140
141 #if __cplusplus >= 201103L
142 tracker_allocator() = default;
143 tracker_allocator(const tracker_allocator&) = default;
144 tracker_allocator(tracker_allocator&&) = default;
145 tracker_allocator& operator=(const tracker_allocator&) = default;
146 tracker_allocator& operator=(tracker_allocator&&) = default;
147
148 // Perfect forwarding constructor.
149 template<typename... _Args>
150 tracker_allocator(_Args&&... __args)
151 : Alloc(std::forward<_Args>(__args)...)
152 { }
153 #else
154 tracker_allocator()
155 { }
156
157 tracker_allocator(const tracker_allocator&)
158 { }
159
160 ~tracker_allocator()
161 { }
162 #endif
163
164 template<class U>
165 tracker_allocator(const tracker_allocator<U,
166 typename AllocTraits::template rebind<U>::other>& alloc)
167 _GLIBCXX_USE_NOEXCEPT
168 : Alloc(alloc)
169 { }
170
171 pointer
172 allocate(size_type n, const void* = 0)
173 {
174 pointer p = AllocTraits::allocate(*this, n);
175 counter_type::allocate(n * sizeof(T));
176 return p;
177 }
178
179 #if __cplusplus >= 201103L
180 template<typename U, typename... Args>
181 void
182 construct(U* p, Args&&... args)
183 {
184 AllocTraits::construct(*this, p, std::forward<Args>(args)...);
185 counter_type::construct();
186 }
187
188 template<typename U>
189 void
190 destroy(U* p)
191 {
192 AllocTraits::destroy(*this, p);
193 counter_type::destroy();
194 }
195 #else
196 void
197 construct(pointer p, const T& value)
198 {
199 AllocTraits::construct(*this, p, value);
200 counter_type::construct();
201 }
202
203 void
204 destroy(pointer p)
205 {
206 AllocTraits::destroy(*this, p);
207 counter_type::destroy();
208 }
209 #endif
210
211 void
212 deallocate(pointer p, size_type num)
213 {
214 counter_type::deallocate(num * sizeof(T));
215 AllocTraits::deallocate(*this, p, num);
216 }
217
218 // Implement swap for underlying allocators that might need it.
219 friend inline void
220 swap(tracker_allocator& a, tracker_allocator& b)
221 {
222 using std::swap;
223
224 Alloc& aa = a;
225 Alloc& ab = b;
226 swap(aa, ab);
227 }
228 };
229
230 template<class T1, class Alloc1, class T2, class Alloc2>
231 bool
232 operator==(const tracker_allocator<T1, Alloc1>& lhs,
233 const tracker_allocator<T2, Alloc2>& rhs) throw()
234 {
235 const Alloc1& alloc1 = lhs;
236 const Alloc2& alloc2 = rhs;
237 return alloc1 == alloc2;
238 }
239
240 template<class T1, class Alloc1, class T2, class Alloc2>
241 bool
242 operator!=(const tracker_allocator<T1, Alloc1>& lhs,
243 const tracker_allocator<T2, Alloc2>& rhs) throw()
244 { return !(lhs == rhs); }
245
246 bool
247 check_construct_destroy(const char* tag, int expected_c, int expected_d);
248
249 template<typename Alloc>
250 bool
251 check_deallocate_null()
252 {
253 // Let's not core here...
254 Alloc a;
255 a.deallocate(0, 1);
256 a.deallocate(0, 10);
257 return true;
258 }
259
260 #if __cpp_exceptions
261 template<typename Alloc>
262 bool
263 check_allocate_max_size()
264 {
265 Alloc a;
266 try
267 {
268 (void) a.allocate(__gnu_test::max_size(a) + 1);
269 }
270 catch(std::bad_alloc&)
271 {
272 return true;
273 }
274 catch(...)
275 {
276 throw;
277 }
278 throw;
279 }
280 #endif
281
282 // A simple allocator which can be constructed endowed of a given
283 // "personality" (an integer), queried in operator== to simulate the
284 // behavior of realworld "unequal" allocators (i.e., not exploiting
285 // the provision in 20.1.5/4, first bullet). A global unordered_map,
286 // filled at allocation time with (pointer, personality) pairs, is
287 // then consulted to enforce the requirements in Table 32 about
288 // deallocation vs allocator equality. Note that this allocator is
289 // swappable, not copy assignable, consistently with Option 3 of DR 431
290 // (see N1599).
291 struct uneq_allocator_base
292 {
293 typedef unord::unordered_map<void*, int> map_type;
294
295 // Avoid static initialization troubles and/or bad interactions
296 // with tests linking testsuite_allocator.o and playing globally
297 // with operator new/delete.
298 static map_type&
299 get_map()
300 {
301 static map_type alloc_map;
302 return alloc_map;
303 }
304 };
305
306 template<typename Tp, typename Alloc = std::allocator<Tp> >
307 class uneq_allocator
308 : private uneq_allocator_base,
309 public Alloc
310 {
311 typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits;
312
313 Alloc& base() { return *this; }
314 const Alloc& base() const { return *this; }
315 void swap_base(Alloc& b) { using std::swap; swap(b, this->base()); }
316
317 public:
318 typedef typename check_consistent_alloc_value_type<Tp, Alloc>::value_type
319 value_type;
320 typedef typename AllocTraits::size_type size_type;
321 typedef typename AllocTraits::pointer pointer;
322
323 #if __cplusplus >= 201103L
324 typedef std::true_type propagate_on_container_swap;
325 typedef std::false_type is_always_equal;
326 #endif
327
328 template<typename Tp1>
329 struct rebind
330 {
331 typedef uneq_allocator<Tp1,
332 typename AllocTraits::template rebind<Tp1>::other> other;
333 };
334
335 uneq_allocator() _GLIBCXX_USE_NOEXCEPT
336 : personality(0) { }
337
338 uneq_allocator(int person) _GLIBCXX_USE_NOEXCEPT
339 : personality(person) { }
340
341 #if __cplusplus >= 201103L
342 uneq_allocator(const uneq_allocator&) = default;
343 uneq_allocator(uneq_allocator&&) = default;
344 #endif
345
346 template<typename Tp1>
347 uneq_allocator(const uneq_allocator<Tp1,
348 typename AllocTraits::template rebind<Tp1>::other>& b)
349 _GLIBCXX_USE_NOEXCEPT
350 : personality(b.get_personality()) { }
351
352 ~uneq_allocator() _GLIBCXX_USE_NOEXCEPT
353 { }
354
355 int get_personality() const { return personality; }
356
357 pointer
358 allocate(size_type n, const void* = 0)
359 {
360 pointer p = AllocTraits::allocate(*this, n);
361
362 try
363 {
364 get_map().insert(map_type::value_type(reinterpret_cast<void*>(p),
365 personality));
366 }
367 catch(...)
368 {
369 AllocTraits::deallocate(*this, p, n);
370 __throw_exception_again;
371 }
372
373 return p;
374 }
375
376 void
377 deallocate(pointer p, size_type n)
378 {
379 VERIFY( p );
380
381 map_type::iterator it = get_map().find(reinterpret_cast<void*>(p));
382 VERIFY( it != get_map().end() );
383
384 // Enforce requirements in Table 32 about deallocation vs
385 // allocator equality.
386 VERIFY( it->second == personality );
387
388 get_map().erase(it);
389 AllocTraits::deallocate(*this, p, n);
390 }
391
392 #if __cplusplus >= 201103L
393 // Not copy assignable...
394 uneq_allocator&
395 operator=(const uneq_allocator&) = delete;
396
397 // ... but still moveable if base allocator is.
398 uneq_allocator&
399 operator=(uneq_allocator&&) = default;
400 #else
401 private:
402 // Not assignable...
403 uneq_allocator&
404 operator=(const uneq_allocator&);
405 #endif
406
407 private:
408 // ... yet swappable!
409 friend inline void
410 swap(uneq_allocator& a, uneq_allocator& b)
411 {
412 std::swap(a.personality, b.personality);
413 a.swap_base(b);
414 }
415
416 template<typename Tp1>
417 friend inline bool
418 operator==(const uneq_allocator& a,
419 const uneq_allocator<Tp1,
420 typename AllocTraits::template rebind<Tp1>::other>& b)
421 { return a.personality == b.get_personality(); }
422
423 template<typename Tp1>
424 friend inline bool
425 operator!=(const uneq_allocator& a,
426 const uneq_allocator<Tp1,
427 typename AllocTraits::template rebind<Tp1>::other>& b)
428 { return !(a == b); }
429
430 int personality;
431 };
432
433 #if __cplusplus >= 201103L
434 // An uneq_allocator which can be used to test allocator propagation.
435 template<typename Tp, bool Propagate, typename Alloc = std::allocator<Tp>>
436 class propagating_allocator : public uneq_allocator<Tp, Alloc>
437 {
438 typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits;
439
440 typedef uneq_allocator<Tp, Alloc> base_alloc;
441 base_alloc& base() { return *this; }
442 const base_alloc& base() const { return *this; }
443 void swap_base(base_alloc& b) { swap(b, this->base()); }
444
445 typedef std::integral_constant<bool, Propagate> trait_type;
446
447 public:
448 // default allocator_traits::rebind_alloc would select
449 // uneq_allocator::rebind so we must define rebind here
450 template<typename Up>
451 struct rebind
452 {
453 typedef propagating_allocator<Up, Propagate,
454 typename AllocTraits::template rebind<Up>::other> other;
455 };
456
457 propagating_allocator(int i) noexcept
458 : base_alloc(i)
459 { }
460
461 template<typename Up>
462 propagating_allocator(const propagating_allocator<Up, Propagate,
463 typename AllocTraits::template rebind<Up>::other>& a)
464 noexcept
465 : base_alloc(a)
466 { }
467
468 propagating_allocator() noexcept = default;
469
470 propagating_allocator(const propagating_allocator&) noexcept = default;
471
472 propagating_allocator&
473 operator=(const propagating_allocator& a) noexcept
474 {
475 static_assert(Propagate, "assigning propagating_allocator<T, true>");
476 propagating_allocator(a).swap_base(*this);
477 return *this;
478 }
479
480 template<bool P2>
481 propagating_allocator&
482 operator=(const propagating_allocator<Tp, P2, Alloc>& a) noexcept
483 {
484 static_assert(P2, "assigning propagating_allocator<T, true>");
485 propagating_allocator(a).swap_base(*this);
486 return *this;
487 }
488
489 // postcondition: LWG2593 a.get_personality() un-changed.
490 propagating_allocator(propagating_allocator&& a) noexcept
491 : base_alloc(std::move(a.base()))
492 { }
493
494 // postcondition: LWG2593 a.get_personality() un-changed
495 propagating_allocator&
496 operator=(propagating_allocator&& a) noexcept
497 {
498 propagating_allocator(std::move(a)).swap_base(*this);
499 return *this;
500 }
501
502 typedef trait_type propagate_on_container_copy_assignment;
503 typedef trait_type propagate_on_container_move_assignment;
504 typedef trait_type propagate_on_container_swap;
505
506 propagating_allocator select_on_container_copy_construction() const
507 { return Propagate ? *this : propagating_allocator(); }
508 };
509
510 // Class template supporting the minimal interface that satisfies the
511 // Allocator requirements, from example in [allocator.requirements]
512 template <class Tp>
513 struct SimpleAllocator
514 {
515 typedef Tp value_type;
516
517 constexpr SimpleAllocator() noexcept { }
518
519 template <class T>
520 SimpleAllocator(const SimpleAllocator<T>&) { }
521
522 Tp *allocate(std::size_t n)
523 { return std::allocator<Tp>().allocate(n); }
524
525 void deallocate(Tp *p, std::size_t n)
526 { std::allocator<Tp>().deallocate(p, n); }
527 };
528
529 template <class T, class U>
530 bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&)
531 { return true; }
532 template <class T, class U>
533 bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&)
534 { return false; }
535
536 template<typename T>
537 struct default_init_allocator
538 {
539 using value_type = T;
540
541 default_init_allocator() = default;
542
543 template<typename U>
544 default_init_allocator(const default_init_allocator<U>& a)
545 : state(a.state)
546 { }
547
548 T*
549 allocate(std::size_t n)
550 { return std::allocator<T>().allocate(n); }
551
552 void
553 deallocate(T* p, std::size_t n)
554 { std::allocator<T>().deallocate(p, n); }
555
556 int state;
557 };
558
559 template<typename T, typename U>
560 bool operator==(const default_init_allocator<T>& t,
561 const default_init_allocator<U>& u)
562 { return t.state == u.state; }
563
564 template<typename T, typename U>
565 bool operator!=(const default_init_allocator<T>& t,
566 const default_init_allocator<U>& u)
567 { return !(t == u); }
568 #endif
569
570 template<typename Tp>
571 struct ExplicitConsAlloc : std::allocator<Tp>
572 {
573 ExplicitConsAlloc() { }
574
575 template<typename Up>
576 explicit
577 ExplicitConsAlloc(const ExplicitConsAlloc<Up>&) { }
578
579 template<typename Up>
580 struct rebind
581 { typedef ExplicitConsAlloc<Up> other; };
582 };
583
584 #if __cplusplus >= 201103L
585 template<typename Tp>
586 class CustomPointerAlloc : public std::allocator<Tp>
587 {
588 template<typename Up, typename Sp = __gnu_cxx::_Std_pointer_impl<Up>>
589 using Ptr = __gnu_cxx::_Pointer_adapter<Sp>;
590
591 public:
592 CustomPointerAlloc() = default;
593
594 template<typename Up>
595 CustomPointerAlloc(const CustomPointerAlloc<Up>&) { }
596
597 template<typename Up>
598 struct rebind
599 { typedef CustomPointerAlloc<Up> other; };
600
601 typedef Ptr<Tp> pointer;
602 typedef Ptr<const Tp> const_pointer;
603 typedef Ptr<void> void_pointer;
604 typedef Ptr<const void> const_void_pointer;
605
606 pointer allocate(std::size_t n, const_void_pointer = {})
607 { return pointer(std::allocator<Tp>::allocate(n)); }
608
609 void deallocate(pointer p, std::size_t n)
610 { std::allocator<Tp>::deallocate(std::addressof(*p), n); }
611 };
612
613 // A class type meeting *only* the Cpp17NullablePointer requirements.
614 // Can be used as a base class for fancy pointers (like PointerBase, below)
615 // or to wrap a built-in pointer type to remove operations not required
616 // by the Cpp17NullablePointer requirements (dereference, increment etc.)
617 template<typename Ptr>
618 struct NullablePointer
619 {
620 // N.B. default constructor does not initialize value
621 NullablePointer() = default;
622 NullablePointer(std::nullptr_t) noexcept : value() { }
623
624 explicit operator bool() const noexcept { return value != nullptr; }
625
626 friend inline bool
627 operator==(NullablePointer lhs, NullablePointer rhs) noexcept
628 { return lhs.value == rhs.value; }
629
630 friend inline bool
631 operator!=(NullablePointer lhs, NullablePointer rhs) noexcept
632 { return lhs.value != rhs.value; }
633
634 protected:
635 explicit NullablePointer(Ptr p) noexcept : value(p) { }
636 Ptr value;
637 };
638
639 // NullablePointer<void> is an empty type that models Cpp17NullablePointer.
640 template<>
641 struct NullablePointer<void>
642 {
643 NullablePointer() = default;
644 NullablePointer(std::nullptr_t) noexcept { }
645 explicit NullablePointer(const volatile void*) noexcept { }
646
647 explicit operator bool() const noexcept { return false; }
648
649 friend inline bool
650 operator==(NullablePointer, NullablePointer) noexcept
651 { return true; }
652
653 friend inline bool
654 operator!=(NullablePointer, NullablePointer) noexcept
655 { return false; }
656 };
657
658 // Utility for use as CRTP base class of custom pointer types
659 template<typename Derived, typename T>
660 struct PointerBase : NullablePointer<T*>
661 {
662 typedef T element_type;
663
664 // typedefs for iterator_traits
665 typedef T value_type;
666 typedef std::ptrdiff_t difference_type;
667 typedef std::random_access_iterator_tag iterator_category;
668 typedef Derived pointer;
669 typedef T& reference;
670
671 using NullablePointer<T*>::NullablePointer;
672
673 // Public (but explicit) constructor from raw pointer:
674 explicit PointerBase(T* p) noexcept : NullablePointer<T*>(p) { }
675
676 template<typename D, typename U,
677 typename = decltype(static_cast<T*>(std::declval<U*>()))>
678 PointerBase(const PointerBase<D, U>& p)
679 : NullablePointer<T*>(p.operator->()) { }
680
681 T& operator*() const { return *this->value; }
682 T* operator->() const { return this->value; }
683 T& operator[](difference_type n) const { return this->value[n]; }
684
685 Derived& operator++() { ++this->value; return derived(); }
686 Derived& operator--() { --this->value; return derived(); }
687
688 Derived operator++(int) { return Derived(this->value++); }
689
690 Derived operator--(int) { return Derived(this->value--); }
691
692 Derived& operator+=(difference_type n)
693 {
694 this->value += n;
695 return derived();
696 }
697
698 Derived& operator-=(difference_type n)
699 {
700 this->value -= n;
701 return derived();
702 }
703
704 Derived
705 operator+(difference_type n) const
706 {
707 Derived p(derived());
708 return p += n;
709 }
710
711 Derived
712 operator-(difference_type n) const
713 {
714 Derived p(derived());
715 return p -= n;
716 }
717
718 private:
719 friend std::ptrdiff_t operator-(PointerBase l, PointerBase r)
720 { return l.value - r.value; }
721
722 friend bool operator<(PointerBase l, PointerBase r)
723 { return l.value < r.value; }
724 friend bool operator>(PointerBase l, PointerBase r)
725 { return l.value > r.value; }
726 friend bool operator<=(PointerBase l, PointerBase r)
727 { return l.value <= r.value; }
728 friend bool operator>=(PointerBase l, PointerBase r)
729 { return l.value >= r.value; }
730
731 Derived&
732 derived() { return static_cast<Derived&>(*this); }
733
734 const Derived&
735 derived() const { return static_cast<const Derived&>(*this); }
736 };
737
738 // implementation for pointer-to-void specializations
739 template<typename T>
740 struct PointerBase_void : NullablePointer<T*>
741 {
742 typedef T element_type;
743
744 // typedefs for iterator_traits
745 typedef T value_type;
746 typedef std::ptrdiff_t difference_type;
747 typedef std::random_access_iterator_tag iterator_category;
748
749 using NullablePointer<T*>::NullablePointer;
750
751 T* operator->() const { return this->value; }
752
753 template<typename D, typename U,
754 typename = decltype(static_cast<T*>(std::declval<U*>()))>
755 PointerBase_void(const PointerBase<D, U>& p)
756 : NullablePointer<T*>(p.operator->()) { }
757 };
758
759 template<typename Derived>
760 struct PointerBase<Derived, void> : PointerBase_void<void>
761 {
762 using PointerBase_void::PointerBase_void;
763 typedef Derived pointer;
764 };
765
766 template<typename Derived>
767 struct PointerBase<Derived, const void> : PointerBase_void<const void>
768 {
769 using PointerBase_void::PointerBase_void;
770 typedef Derived pointer;
771 };
772 #endif // C++11
773
774 #if __cplusplus >= 201703L
775 #if __cpp_aligned_new
776 // A concrete memory_resource, with error checking.
777 class memory_resource : public std::pmr::memory_resource
778 {
779 public:
780 memory_resource()
781 : lists(new allocation_lists)
782 { }
783
784 memory_resource(const memory_resource& r) noexcept
785 : lists(r.lists)
786 { lists->refcount++; }
787
788 memory_resource& operator=(const memory_resource&) = delete;
789
790 ~memory_resource()
791 {
792 if (lists->refcount-- == 1)
793 delete lists; // last one out turns out the lights
794 }
795
796 struct bad_size { };
797 struct bad_alignment { };
798 struct bad_address { };
799
800 // Deallocate everything (moving the tracking info to the freed list)
801 void
802 deallocate_everything()
803 {
804 while (lists->active)
805 {
806 auto a = lists->active;
807 // Intentionally virtual dispatch, to inform derived classes:
808 this->do_deallocate(a->p, a->bytes, a->alignment);
809 }
810 }
811
812 // Clear the freed list
813 void
814 forget_freed_allocations()
815 { lists->forget_allocations(lists->freed); }
816
817 // Count how many allocations have been done and not freed.
818 std::size_t
819 number_of_active_allocations() const noexcept
820 {
821 std::size_t n = 0;
822 for (auto a = lists->active; a != nullptr; a = a->next)
823 ++n;
824 return n;
825 }
826
827 protected:
828 void*
829 do_allocate(std::size_t bytes, std::size_t alignment) override
830 {
831 // TODO perform a single allocation and put the allocation struct
832 // in the buffer using placement new? It means deallocation won't
833 // actually return memory to the OS, as it will stay in lists->freed.
834 //
835 // TODO adjust the returned pointer to be minimally aligned?
836 // e.g. if alignment==1 don't return something aligned to 2 bytes.
837 // Maybe not worth it, at least monotonic_buffer_resource will
838 // never ask upstream for anything with small alignment.
839 void* p = ::operator new(bytes, std::align_val_t(alignment));
840 lists->active = new allocation{p, bytes, alignment, lists->active};
841 return p;
842 }
843
844 void
845 do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override
846 {
847 allocation** aptr = &lists->active;
848 while (*aptr)
849 {
850 allocation* a = *aptr;
851 if (p == a->p)
852 {
853 if (bytes != a->bytes)
854 _S_throw<bad_size>();
855 if (alignment != a->alignment)
856 _S_throw<bad_alignment>();
857 #if __cpp_sized_deallocation
858 ::operator delete(p, bytes, std::align_val_t(alignment));
859 #else
860 ::operator delete(p, std::align_val_t(alignment));
861 #endif
862 *aptr = a->next;
863 a->next = lists->freed;
864 lists->freed = a;
865 return;
866 }
867 aptr = &a->next;
868 }
869 _S_throw<bad_address>();
870 }
871
872 bool
873 do_is_equal(const std::pmr::memory_resource& r) const noexcept override
874 {
875 #if __cpp_rtti
876 // Equality is determined by sharing the same allocation_lists object.
877 if (auto p = dynamic_cast<const memory_resource*>(&r))
878 return p->lists == lists;
879 #else
880 if (this == &r) // Is this the best we can do without RTTI?
881 return true;
882 #endif
883 return false;
884 }
885
886 private:
887 template<typename E>
888 static void
889 _S_throw()
890 {
891 #if __cpp_exceptions
892 throw E();
893 #else
894 __builtin_abort();
895 #endif
896 }
897
898 struct allocation
899 {
900 void* p;
901 std::size_t bytes;
902 std::size_t alignment;
903 allocation* next;
904 };
905
906 // Maintain list of allocated blocks and list of freed blocks.
907 // Copies of this memory_resource share the same ref-counted lists.
908 struct allocation_lists
909 {
910 unsigned refcount = 1;
911 allocation* active = nullptr;
912 allocation* freed = nullptr;
913
914 void forget_allocations(allocation*& list)
915 {
916 while (list)
917 {
918 auto p = list;
919 list = list->next;
920 delete p;
921 }
922 }
923
924 ~allocation_lists()
925 {
926 forget_allocations(active); // Anything in this list is a leak!
927 forget_allocations(freed);
928 }
929 };
930
931 allocation_lists* lists;
932 };
933 #endif // aligned-new
934
935 // Set the default resource, and restore the previous one on destruction.
936 struct default_resource_mgr
937 {
938 explicit default_resource_mgr(std::pmr::memory_resource* r)
939 : prev(std::pmr::set_default_resource(r))
940 { }
941
942 ~default_resource_mgr()
943 { std::pmr::set_default_resource(prev); }
944
945 std::pmr::memory_resource* prev;
946 };
947
948 #endif // C++17
949
950 } // namespace __gnu_test
951
952 #endif // _GLIBCXX_TESTSUITE_ALLOCATOR_H