GCC Code Coverage Report


Directory: ./
File: include/na64util/mem/alloc.hh
Date: 2025-09-01 06:19:01
Exec Total Coverage
Lines: 15 15 100.0%
Functions: 61 121 50.4%
Branches: 1 2 50.0%

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