Coverage Report

Created: 2024-07-18 11:41

src/zserio/pmr/PolymorphicAllocator.h
Line
Count
Source
1
#ifndef ZSERIO_PMR_POLYMORPHIC_ALLOCATOR_H_INC
2
#define ZSERIO_PMR_POLYMORPHIC_ALLOCATOR_H_INC
3
4
#include <cstddef>
5
#include <limits>
6
#include <type_traits>
7
#include <utility>
8
9
#include "zserio/Types.h"
10
#include "zserio/pmr/MemoryResource.h"
11
12
namespace zserio
13
{
14
namespace pmr
15
{
16
namespace detail
17
{
18
19
/**
20
 * Polymorphic allocator inspired by C++17 standard.
21
 */
22
template <class T = uint8_t>
23
class PolymorphicAllocatorBase
24
{
25
public:
26
    using value_type = T;
27
28
    // Following typedefs are only present for compatibility with older std. libraries, that does not fully
29
    // conform to C++11. [old-compiler-support]
30
    using pointer = value_type*;
31
    using const_pointer = const value_type*;
32
    using size_type = std::size_t;
33
    using difference_type = std::ptrdiff_t;
34
    using reference = value_type&;
35
    using const_reference = const value_type&;
36
37
    /**
38
     * Constructor.
39
     *
40
     * Note that this is intentionally non-explicit to allow to pass MemoryResource wherever
41
     * the PolymorphicAllocator is required.
42
     *
43
     * \param resource Memory resource. According to the C++ standard the resource may not be NULL. Since it
44
     *                 implies undefined behaviour, we define a non-standard extension here. When the resource
45
     *                 is NULL, getDefaultResource() is used instead!
46
     */
47
    PolymorphicAllocatorBase(MemoryResource* resource = getDefaultResource()) noexcept :
48
            m_resource(resource != nullptr ? resource : getDefaultResource()) // non-standard extension
49
142
    {}
50
51
    /**
52
     * Method generated by default.
53
     * \{
54
     */
55
    ~PolymorphicAllocatorBase() = default;
56
57
    PolymorphicAllocatorBase(const PolymorphicAllocatorBase& other) noexcept = default;
58
    PolymorphicAllocatorBase& operator=(const PolymorphicAllocatorBase& other) noexcept = default;
59
60
    PolymorphicAllocatorBase(PolymorphicAllocatorBase&& other) noexcept = default;
61
    PolymorphicAllocatorBase& operator=(PolymorphicAllocatorBase&& other) noexcept = default;
62
    /**
63
     * \}
64
     */
65
66
    /**
67
     * Copy constructor from PolymorphicAllocator with another value_type.
68
     *
69
     * \param other Other PolymorphicAllocator.
70
     */
71
    template <class U>
72
    PolymorphicAllocatorBase(const PolymorphicAllocatorBase<U>& other) noexcept :
73
            m_resource(other.resource())
74
402
    {}
75
76
    /**
77
     * Assignment operator from PolymorphicAllocator with another value_type.
78
     *
79
     * \param other Other PolymorphicAllocator.
80
     */
81
    template <class U>
82
    PolymorphicAllocatorBase& operator=(const PolymorphicAllocatorBase<U>& other) noexcept
83
    {
84
        m_resource = other.resource();
85
        return *this;
86
    }
87
88
    /**
89
     * Allocates memory for n values.
90
     *
91
     * \param size Number of values to allocate memory for.
92
     */
93
    value_type* allocate(std::size_t size)
94
196
    {
95
196
        return static_cast<value_type*>(m_resource->allocate(size * sizeof(value_type), alignof(value_type)));
96
196
    }
97
98
    /**
99
     * Deallocates memory for n values.
100
     *
101
     * \param memory Pointer to the memory to deallocate.
102
     * \param size Number of values held by the memory pointed to by memory.
103
     *         Shall be the same size as was used for allocation of memory.
104
     */
105
    void deallocate(value_type* memory, std::size_t size) noexcept
106
196
    {
107
196
        m_resource->deallocate(memory, size * sizeof(value_type), alignof(value_type));
108
196
    }
109
110
    /**
111
     * Gets the underlying memory resource.
112
     */
113
    MemoryResource* resource() const noexcept
114
554
    {
115
554
        return m_resource;
116
554
    }
117
118
    /**
119
     * Constructs an object in allocated memory.
120
     *
121
     * \param ptr Pointer to memory where the object will be constructed.
122
     * \param args Parameters to be forwarded to the object constructor.
123
     * \note This is only necessary for compatibility with older std. libraries, that does not fully
124
     * conform to C++11. [old-compiler-support]
125
     */
126
    template <typename U, typename... Args>
127
    void construct(U* ptr, Args&&... args) noexcept(
128
            noexcept(new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...)))
129
214
    {
130
214
        new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...);
131
214
    }
132
133
    /**
134
     * Destroys an object
135
     *
136
     * \param ptr Pointer to the object to be destroyed.
137
     * \note This is only necessary for compatibility with older std. libraries, that does not fully
138
     * conform to C++11. [old-compiler-support]
139
     */
140
    template <typename U>
141
    void destroy(U* ptr) noexcept(noexcept(ptr->~U()))
142
214
    {
143
214
        ptr->~U();
144
214
    }
145
146
    /**
147
     * Returns theoretical maximal limit for allocation.
148
     *
149
     * \return Theoretical maximal limit for allocation.
150
     * \note This is only necessary for compatibility with older std. libraries, that does not fully
151
     * conform to C++11. [old-compiler-support]
152
     */
153
    size_type max_size() const noexcept
154
129
    {
155
129
        return std::numeric_limits<size_type>::max() / sizeof(value_type);
156
129
    }
157
158
private:
159
    MemoryResource* m_resource;
160
};
161
162
template <class T, class U>
163
bool operator==(const PolymorphicAllocatorBase<T>& lhs, const PolymorphicAllocatorBase<U>& rhs) noexcept
164
68
{
165
68
    return *lhs.resource() == *rhs.resource();
166
68
}
167
168
template <class T, class U>
169
bool operator!=(const PolymorphicAllocatorBase<T>& lhs, const PolymorphicAllocatorBase<U>& rhs) noexcept
170
6
{
171
6
    return !(lhs == rhs);
172
6
}
173
174
} // namespace detail
175
176
/**
177
 * Non-propagating version of the polymorphic allocator. This allocator behaves as the polymorphic_allocator
178
 * from C++17.
179
 */
180
template <class T = uint8_t>
181
class PolymorphicAllocator : public detail::PolymorphicAllocatorBase<T>
182
{
183
public:
184
    using detail::PolymorphicAllocatorBase<T>::PolymorphicAllocatorBase;
185
186
    using propagate_on_container_copy_assignment = std::false_type;
187
    using propagate_on_container_move_assignment = std::false_type;
188
    using propagate_on_container_swap = std::false_type;
189
190
    /**
191
     * Returns instance of the allocator to be used when a container gets copied.
192
     */
193
    PolymorphicAllocator select_on_container_copy_construction() const
194
1
    {
195
1
        return PolymorphicAllocator();
196
1
    }
197
198
    /**
199
     * Rebind template.
200
     * \note This is only necessary for compatibility with older std. libraries, that does not fully
201
     * conform to C++11. [old-compiler-support]
202
     */
203
    template <typename U>
204
    struct rebind
205
    {
206
        using other = PolymorphicAllocator<U>;
207
    };
208
};
209
210
/**
211
 * Propagating version of the polymorphic allocator. This one is propagated on container copy and assignment.
212
 */
213
template <class T = uint8_t>
214
class PropagatingPolymorphicAllocator : public detail::PolymorphicAllocatorBase<T>
215
{
216
public:
217
    using detail::PolymorphicAllocatorBase<T>::PolymorphicAllocatorBase;
218
219
    using propagate_on_container_copy_assignment = std::true_type;
220
    using propagate_on_container_move_assignment = std::true_type;
221
    using propagate_on_container_swap = std::true_type;
222
223
    /**
224
     * Returns instance of the allocator to be used when a container gets copied.
225
     */
226
    PropagatingPolymorphicAllocator select_on_container_copy_construction() const
227
29
    {
228
29
        return *this;
229
29
    }
230
231
    /**
232
     * Rebind template.
233
     * \note This is only necessary for compatibility with older std. libraries, that does not fully
234
     * conform to C++11. [old-compiler-support]
235
     */
236
    template <typename U>
237
    struct rebind
238
    {
239
        using other = PropagatingPolymorphicAllocator<U>;
240
    };
241
};
242
243
} // namespace pmr
244
} // namespace zserio
245
246
#endif // ZSERIO_PMR_POLYMORPHIC_ALLOCATOR_H_INC