| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "na64sw-config.h" | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <stdexcept> | ||
| 7 | |||
| 8 | /**\file | ||
| 9 | * \brief Pool allocators | ||
| 10 | * | ||
| 11 | * These classes express the API to manage memory block in a bit less generic | ||
| 12 | * way than is provided by default STL allocators. | ||
| 13 | * | ||
| 14 | * We presume that in the frame of memory allocation strategy, all | ||
| 15 | * type-specialized allocators refer to single object (called "Strategy") which | ||
| 16 | * alone maintains allocate/create/destroy lifecycle. This is a deduction | ||
| 17 | * of what default STL logic foresees (where one can specify particular | ||
| 18 | * allocators for every type with `allocator_traits`). | ||
| 19 | * | ||
| 20 | * This API simplifies usage of custom allocators. | ||
| 21 | * For example usage see `PlainBlock`. | ||
| 22 | * */ | ||
| 23 | |||
| 24 | namespace na64dp { | ||
| 25 | namespace mem { | ||
| 26 | |||
| 27 | /// A pool for a single type, acting as a single-type allocator | ||
| 28 | /// | ||
| 29 | /// Internally, keeps reference to a current `StrategyT` instance which, | ||
| 30 | /// in fact, manages memory access. | ||
| 31 | /// | ||
| 32 | /// \todo Use a "malfunction" flag instead of throwing in ctr | ||
| 33 | template<typename T, typename StrategyT> | ||
| 34 | class Allocator { | ||
| 35 | protected: | ||
| 36 | StrategyT * _pools; | ||
| 37 | public: | ||
| 38 | // NOTE: starting from v2.44+ default constructor is implement, although | ||
| 39 | // raises exception upon construction. We were forced to do so | ||
| 40 | // to support `std::vector<>` container which requires allocator | ||
| 41 | // to be constructible | ||
| 42 | //Allocator() = delete; | ||
| 43 | Allocator() : _pools(nullptr) { | ||
| 44 | throw std::runtime_error("Default allocator constructor called."); | ||
| 45 | } | ||
| 46 | 635 | Allocator(StrategyT & pools) : _pools(&pools) {} | |
| 47 | 894 | Allocator(const Allocator<T, StrategyT> & other) : _pools(other._pools) {} | |
| 48 | |||
| 49 | // compiler made me do this! I expected it rather use traits::rebind(), | ||
| 50 | // but it seems to ignore it. | ||
| 51 | Allocator & operator=(const Allocator<T, StrategyT> & other) | ||
| 52 | { _pools = const_cast<Allocator<T, StrategyT>&>(other)._pools; | ||
| 53 | return *this; } | ||
| 54 | 820 | template<typename otherT> Allocator( const Allocator<otherT, StrategyT> & other ) | |
| 55 | 820 | : _pools(const_cast<Allocator<otherT, StrategyT>&>(other)._pools) {} | |
| 56 | |||
| 57 | /// required typedef for `std::allocator` implementation | ||
| 58 | using value_type = T; | ||
| 59 | protected: // (except friend traits) | ||
| 60 | 67 | T * acquire(typename StrategyT::Size_t sz) { return _pools->template acquire<T>(sz); } | |
| 61 | 86 | void release( T* p, typename StrategyT::Size_t s) { _pools->template release<T>(p, s); } | |
| 62 | typename StrategyT::Size_t remaining_capacity() const noexcept { return _pools->template max_for<T>(); } | ||
| 63 | |||
| 64 | // I'm not a master of friendship, may be this are an overkill. | ||
| 65 | // In principle, friends must be restricted with same type of | ||
| 66 | // `StrategyT', but T/otherT may vary. | ||
| 67 | template<typename A> friend class ::std::allocator_traits; | ||
| 68 | template<typename otherT, typename StrategyT2> friend class Allocator; | ||
| 69 | public: | ||
| 70 | bool operator!=(const Allocator<T, StrategyT> & other) const { | ||
| 71 | return _pools != other._pools; | ||
| 72 | } | ||
| 73 | |||
| 74 | template<typename U> struct rebind { using other = Allocator<U, StrategyT>; }; // experimental | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // namespace ::na64dp::mem | ||
| 78 | } // namespace na64dp | ||
| 79 | |||
| 80 | // this must define `constexpr' for C++20 onwards; dunno compiler macro | ||
| 81 | // to control this at the moment | ||
| 82 | # define CPP20_ALLOC_CONSTEXPR | ||
| 83 | namespace std { | ||
| 84 | template<typename ObjT, typename StrategyT> | ||
| 85 | struct allocator_traits< na64dp::mem::Allocator<ObjT, StrategyT> > { | ||
| 86 | typedef na64dp::mem::Allocator<ObjT, StrategyT> allocator_type; | ||
| 87 | typedef ObjT value_type; | ||
| 88 | typedef value_type* pointer; | ||
| 89 | typedef const value_type* const_pointer; | ||
| 90 | typedef void * void_pointer; | ||
| 91 | typedef value_type& reference; | ||
| 92 | typedef const value_type& const_reference; | ||
| 93 | typedef typename StrategyT::Size_t difference_type; // we SURE it is isgned | ||
| 94 | typedef typename StrategyT::Size_t size_type; | ||
| 95 | |||
| 96 | typedef std::true_type propagate_on_container_copy_assignment; | ||
| 97 | typedef std::true_type propagate_on_container_move_assignment; | ||
| 98 | typedef std::true_type propagate_on_container_swap; | ||
| 99 | typedef std::false_type is_always_equal; // (since c++17) | ||
| 100 | |||
| 101 | 67 | static pointer allocate( allocator_type & pool | |
| 102 | , size_type n ) { | ||
| 103 | 67 | return pool.acquire(n); | |
| 104 | } | ||
| 105 | |||
| 106 | 86 | static CPP20_ALLOC_CONSTEXPR void deallocate( allocator_type & pool | |
| 107 | , pointer ptr | ||
| 108 | , size_type n ) { | ||
| 109 | 86 | pool.release( ptr, n ); | |
| 110 | } | ||
| 111 | |||
| 112 | template< class T, class... Args > | ||
| 113 | 59 | static CPP20_ALLOC_CONSTEXPR void construct( allocator_type & pool | |
| 114 | , T* ptr | ||
| 115 | , Args&&... args ) { | ||
| 116 |
1/2✓ Branch 3 taken 29 times.
✗ Branch 6 not taken.
|
59 | ::new (static_cast<void*>(ptr)) T(std::forward<Args>(args)...); |
| 117 | 59 | } | |
| 118 | |||
| 119 | template< class T > static CPP20_ALLOC_CONSTEXPR | ||
| 120 | 78 | void destroy( allocator_type & pool | |
| 121 | , T* p ) { | ||
| 122 | 78 | p->~T(); | |
| 123 | } | ||
| 124 | |||
| 125 | static CPP20_ALLOC_CONSTEXPR size_type max_size( const allocator_type & pool ) noexcept { | ||
| 126 | return pool.remaining_capacity(); | ||
| 127 | } | ||
| 128 | |||
| 129 | //static CPP20_ALLOC_CONSTEXPR allocator_type | ||
| 130 | //select_on_container_copy_construction(const allocator_type & o) | ||
| 131 | //{ return o; } | ||
| 132 | |||
| 133 | template<typename T> using rebind_alloc | ||
| 134 | = na64dp::mem::Allocator<T, StrategyT>; | ||
| 135 | template<typename T> using rebind_traits = std::allocator_traits< rebind_alloc<T> >; | ||
| 136 | |||
| 137 | // TODO: perhaps, this is not appropriate behaviour (in STL this is | ||
| 138 | // implemented with SFINAE), but the intention was to prohibit allocator | ||
| 139 | // copy construction. | ||
| 140 | static allocator_type | ||
| 141 | select_on_container_copy_construction( const allocator_type & a ) { | ||
| 142 | return a; | ||
| 143 | } | ||
| 144 | //{ // get allocator to use | ||
| 145 | //return (_Alloc_select::_Fn(0, _Al)); | ||
| 146 | //} | ||
| 147 | }; | ||
| 148 | } // namespace std | ||
| 149 | #undef CPP20_ALLOC_CONSTEXPR | ||
| 150 | |||
| 151 |