1 |
|
|
#ifndef ZSERIO_TRACKING_ALLOCATOR_H_INC |
2 |
|
|
#define ZSERIO_TRACKING_ALLOCATOR_H_INC |
3 |
|
|
|
4 |
|
|
#include <memory> |
5 |
|
|
#include <set> |
6 |
|
|
|
7 |
|
|
#include "gtest/gtest.h" |
8 |
|
|
|
9 |
|
|
namespace zserio |
10 |
|
|
{ |
11 |
|
|
|
12 |
|
|
namespace detail |
13 |
|
|
{ |
14 |
|
|
|
15 |
|
|
class AllocationTracker |
16 |
|
|
{ |
17 |
|
|
public: |
18 |
|
234 |
AllocationTracker() = default; |
19 |
|
234 |
~AllocationTracker() |
20 |
|
234 |
{ |
21 |
✗✓ |
234 |
EXPECT_TRUE(m_allocations.empty()); |
22 |
|
234 |
} |
23 |
|
|
|
24 |
|
|
AllocationTracker(const AllocationTracker&) = default; |
25 |
|
|
AllocationTracker& operator=(const AllocationTracker&) = default; |
26 |
|
|
|
27 |
|
|
AllocationTracker(AllocationTracker&&) = default; |
28 |
|
|
AllocationTracker& operator=(AllocationTracker&&) = default; |
29 |
|
|
|
30 |
|
217 |
void allocated(void* ptr) |
31 |
|
|
{ |
32 |
|
217 |
m_allocations.insert(ptr); |
33 |
|
217 |
} |
34 |
|
|
|
35 |
|
217 |
void deallocated(void* ptr) |
36 |
|
|
{ |
37 |
✓✗✗✓ ✗✗✗✗ ✗✗✗✗
|
217 |
EXPECT_TRUE(m_allocations.count(ptr) > 0); |
38 |
|
217 |
m_allocations.erase(ptr); |
39 |
|
217 |
} |
40 |
|
|
|
41 |
|
253 |
size_t numAllocs() const |
42 |
|
|
{ |
43 |
|
253 |
return m_allocations.size(); |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
private: |
47 |
|
|
std::set<void*> m_allocations; |
48 |
|
|
}; |
49 |
|
|
|
50 |
|
|
} // namespace detail |
51 |
|
|
|
52 |
|
|
/** |
53 |
|
|
* Stateful allocator that tracks the allocations. Used in unit tests. |
54 |
|
|
* Every copy of an allocator must be able to deallocate what any of the other copies |
55 |
|
|
* allocated. This allocator simulates this behavior, all copies shares the same tracker, |
56 |
|
|
* so it can check if the allocators are used correctly (ie. no allocations leaks, |
57 |
|
|
* every allocation is deallocated by the same allocator or its copy). |
58 |
|
|
*/ |
59 |
|
|
template <typename T> |
60 |
|
49 |
class TrackingAllocatorImpl |
61 |
|
|
{ |
62 |
|
|
public: |
63 |
|
|
using value_type = typename std::allocator<T>::value_type; |
64 |
|
|
using pointer = typename std::allocator<T>::pointer; |
65 |
|
|
|
66 |
|
234 |
TrackingAllocatorImpl() |
67 |
✓✗✓✗ ✓✗ |
234 |
: m_tracker(std::make_shared<detail::AllocationTracker>()) |
68 |
|
234 |
{} |
69 |
|
1595 |
~TrackingAllocatorImpl() = default; |
70 |
|
|
|
71 |
|
1028 |
TrackingAllocatorImpl(const TrackingAllocatorImpl&) = default; |
72 |
|
|
TrackingAllocatorImpl& operator=(const TrackingAllocatorImpl&) = default; |
73 |
|
|
|
74 |
|
|
TrackingAllocatorImpl(TrackingAllocatorImpl&&) = delete; |
75 |
|
|
TrackingAllocatorImpl& operator=(TrackingAllocatorImpl&&) = delete; |
76 |
|
|
|
77 |
|
|
template <typename Other> |
78 |
|
333 |
TrackingAllocatorImpl(const TrackingAllocatorImpl<Other>& other) : |
79 |
|
|
m_allocator(other.m_allocator), |
80 |
|
333 |
m_tracker(other.m_tracker) |
81 |
|
333 |
{} |
82 |
|
|
|
83 |
|
217 |
value_type* allocate(std::size_t n) |
84 |
|
|
{ |
85 |
|
217 |
const auto ptr = m_allocator.allocate(n); |
86 |
|
217 |
m_tracker->allocated(ptr); |
87 |
|
217 |
return ptr; |
88 |
|
|
} |
89 |
|
|
|
90 |
|
217 |
void deallocate(value_type* p, std::size_t n) noexcept |
91 |
|
|
{ |
92 |
|
217 |
m_tracker->deallocated(p); |
93 |
|
217 |
m_allocator.deallocate(p, n); |
94 |
|
217 |
} |
95 |
|
|
|
96 |
|
101 |
bool operator==(const TrackingAllocatorImpl& other) const |
97 |
|
|
{ |
98 |
|
101 |
return std::tie(m_tracker, m_allocator) == std::tie(other.m_tracker, other.m_allocator); |
99 |
|
|
} |
100 |
|
|
|
101 |
|
7 |
bool operator!=(const TrackingAllocatorImpl& other) const |
102 |
|
|
{ |
103 |
|
7 |
return !(*this == other); |
104 |
|
|
} |
105 |
|
|
|
106 |
|
253 |
size_t numAllocs() const |
107 |
|
|
{ |
108 |
|
253 |
return m_tracker->numAllocs(); |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
template <typename U> |
112 |
|
|
friend class TrackingAllocatorImpl; |
113 |
|
|
|
114 |
|
|
private: |
115 |
|
|
std::allocator<T> m_allocator; |
116 |
|
|
std::shared_ptr<detail::AllocationTracker> m_tracker; |
117 |
|
|
}; |
118 |
|
|
|
119 |
|
|
/** |
120 |
|
|
* Propagating allocator. Ie. container copy/move propagates also |
121 |
|
|
* the allocator. |
122 |
|
|
*/ |
123 |
|
|
template <typename T> |
124 |
|
1585 |
class TrackingAllocator : public TrackingAllocatorImpl<T> |
125 |
|
|
{ |
126 |
|
|
public: |
127 |
|
|
using propagate_on_container_copy_assignment = std::true_type; |
128 |
|
|
using propagate_on_container_move_assignment = std::true_type; |
129 |
|
|
|
130 |
|
159 |
using TrackingAllocatorImpl<T>::TrackingAllocatorImpl; |
131 |
|
|
|
132 |
|
30 |
TrackingAllocator select_on_container_copy_construction() const |
133 |
|
|
{ |
134 |
|
30 |
return *this; |
135 |
|
|
} |
136 |
|
|
}; |
137 |
|
|
|
138 |
|
|
/** |
139 |
|
|
* Non-propagating allocator. Ie. container copy/move does not propagate |
140 |
|
|
* the allocator. |
141 |
|
|
*/ |
142 |
|
|
template <typename T> |
143 |
|
1321 |
class TrackingAllocatorNonProp : public TrackingAllocatorImpl<T> |
144 |
|
|
{ |
145 |
|
|
public: |
146 |
|
|
using propagate_on_container_copy_assignment = std::false_type; |
147 |
|
|
using propagate_on_container_move_assignment = std::false_type; |
148 |
|
|
|
149 |
|
174 |
using TrackingAllocatorImpl<T>::TrackingAllocatorImpl; |
150 |
|
|
|
151 |
|
27 |
TrackingAllocatorNonProp select_on_container_copy_construction() const |
152 |
|
|
{ |
153 |
|
27 |
return TrackingAllocatorNonProp(); |
154 |
|
|
} |
155 |
|
|
}; |
156 |
|
|
|
157 |
|
|
} // namespace zserio |
158 |
|
|
|
159 |
|
|
#endif // ZSERIO_TRACKING_ALLOCATOR_H_INC |