KTL
Loading...
Searching...
No Matches
cascading.h
Go to the documentation of this file.
1#pragma once
2
3#include "../utility/aligned_malloc.h"
4#include "../utility/alignment.h"
5#include "../utility/assert.h"
6#include "../utility/empty_base.h"
7#include "../utility/meta.h"
8#include "../utility/source_location.h"
9#include "cascading_fwd.h"
10#include "type_allocator.h"
11
12#include <memory>
13#include <type_traits>
14
15namespace ktl
16{
27 template<typename Alloc>
29 {
30 private:
31 static_assert(detail::has_no_value_type_v<Alloc>, "Building on top of typed allocators is not allowed. Use allocators without a type");
32 static_assert(detail::has_owns_v<Alloc>, "The allocator is required to have an 'owns(void*)' method");
33
34 public:
36
37 private:
38 struct node
39 {
40 KTL_EMPTY_BASE Alloc Allocator;
41 size_type Allocations = 0;
42 node* Next = nullptr;
43 };
44
45 public:
46 cascading() noexcept :
47 m_Node(nullptr) {}
48
49 cascading(const cascading&) = delete;
50
52 noexcept(std::is_nothrow_move_constructible_v<node>) :
53 m_Node(std::move(other.m_Node))
54 {
55 other.m_Node = nullptr;
56 }
57
59 {
60 release();
61 }
62
63 cascading& operator=(const cascading&) = delete;
64
66 noexcept(std::is_nothrow_move_assignable_v<node>)
67 {
68 release();
69
70 m_Node = std::move(rhs.m_Node);
71
72 rhs.m_Node = nullptr;
73
74 return *this;
75 }
76
77 bool operator==(const cascading& rhs) const noexcept
78 {
79 return m_Node == rhs.m_Node;
80 }
81
82 bool operator!=(const cascading& rhs) const noexcept
83 {
84 return m_Node != rhs.m_Node;
85 }
86
87#pragma region Allocation
94 void* allocate(size_type n, const source_location source = KTL_SOURCE()) noexcept(
95 std::is_nothrow_default_constructible_v<node> &&
96 detail::has_nothrow_allocate_v<Alloc> &&
97 (!detail::has_max_size_v<Alloc> || detail::has_nothrow_max_size_v<Alloc>))
98 {
99 // Add an initial allocator
100 if (!m_Node)
102
103 if constexpr (detail::has_max_size_v<Alloc>)
104 {
105 if (n > m_Node->Allocator.max_size())
106 return nullptr;
107 }
108
109 void* p = detail::allocate(m_Node->Allocator, n, source);
110
111 // If the allocator was unable to allocate it, create a new one
112 if (p == nullptr)
113 {
114 node* next = m_Node;
115
117 m_Node->Next = next;
118
119 p = detail::allocate(m_Node->Allocator, n, source);
120 }
121
122 if (p)
123 m_Node->Allocations++;
124
125 return p;
126 }
127
134 void deallocate(void* p, size_type n) noexcept(
135 std::is_nothrow_destructible_v<node> &&
138 {
139 KTL_ASSERT(p != nullptr);
140
141 node* prev = nullptr;
142 node* next = m_Node;
143 while (next)
144 {
145 if (next->Allocator.owns(p))
146 {
147 next->Allocator.deallocate(p, n);
148
149 // If this allocator holds no allocations then delete it
150 // Unless it's the main one, in which case keep it
151 if (--next->Allocations == 0 && prev)
152 {
153 prev->Next = next->Next;
154
156 }
157
158 break;
159 }
160
161 prev = next;
162 next = next->Next;
163 }
164 }
165#pragma endregion
166
167#pragma region Construction
175 template<typename T, typename... Args>
176 typename std::enable_if<detail::has_construct_v<Alloc, T*, Args...>, void>::type
177 construct(T* p, Args&&... args) noexcept(
179 detail::has_nothrow_construct_v<Alloc, T*, Args...>)
180 {
181 node* next = m_Node;
182 while (next)
183 {
184 if (next->Allocator.owns(p))
185 {
186 next->Allocator.construct(p, std::forward<Args>(args)...);
187 return;
188 }
189
190 next = next->Next;
191 }
192
193 // If we ever get to this point, something has gone wrong with the internal allocators
194 KTL_ASSERT(false);
195
196 ::new(p) T(std::forward<Args>(args)...);
197 }
198
204 template<typename T>
205 typename std::enable_if<detail::has_destroy_v<Alloc, T*>, void>::type
206 destroy(T* p) noexcept(
209 {
210 node* next = m_Node;
211 while (next)
212 {
213 if (next->Allocator.owns(p))
214 {
215 next->Allocator.destroy(p);
216 return;
217 }
218
219 next = next->Next;
220 }
221
222 // If we ever get to this point, something has gone wrong with the internal allocators
223 KTL_ASSERT(false);
224
225 p->~T();
226 }
227#pragma endregion
228
229#pragma region Utility
235 template<typename A = Alloc>
236 typename std::enable_if<detail::has_max_size_v<A>, size_type>::type
237 max_size() const
238 noexcept(detail::has_nothrow_max_size_v<A>)
239 {
240 return m_Node->Allocator.max_size();
241 }
242
248 bool owns(void* p) const
250 {
251 node* next = m_Node;
252 while (next)
253 {
254 if (next->Allocator.owns(p))
255 return true;
256
257 next = next->Next;
258 }
259
260 return false;
261 }
262#pragma endregion
263
264 private:
265 void release()
266 noexcept(std::is_nothrow_destructible_v<node>)
267 {
268 node* next = m_Node;
269 while (next)
270 {
271 // Assert that we only have a single allocator left
272 // Otherwise someone forgot to deallocate memory
273 // This isn't a hard-error though
274 KTL_ASSERT(next == m_Node);
275
276 node* current = next;
277
278 next = current->Next;
279
280 detail::aligned_delete(current);
281 }
282 }
283
284 private:
285 node* m_Node;
286 };
287}
#define KTL_ASSERT(x)
Definition assert.h:17
An allocator which owns multiple instances of a given sub allocator. When allocating it will attempt ...
Definition cascading.h:29
void deallocate(void *p, size_type n) noexcept(std::is_nothrow_destructible_v< node > &&detail::has_nothrow_owns_v< Alloc > &&detail::has_nothrow_deallocate_v< Alloc >)
Attempts to deallocate the memory at location p.
Definition cascading.h:134
std::enable_if< detail::has_max_size_v< A >, size_type >::type max_size() const noexcept(detail::has_nothrow_max_size_v< A >)
Returns the maximum size that an allocation can be.
Definition cascading.h:237
bool operator==(const cascading &rhs) const noexcept
Definition cascading.h:77
cascading & operator=(const cascading &)=delete
cascading(const cascading &)=delete
bool owns(void *p) const noexcept(detail::has_nothrow_owns_v< Alloc >)
Returns whether or not the allocator owns the given location in memory.
Definition cascading.h:248
detail::get_size_type_t< Alloc > size_type
Definition cascading.h:35
std::enable_if< detail::has_construct_v< Alloc, T *, Args... >, void >::type construct(T *p, Args &&... args) noexcept(detail::has_nothrow_owns_v< Alloc > &&detail::has_nothrow_construct_v< Alloc, T *, Args... >)
Constructs an object of T with the given ...args at the given location.
Definition cascading.h:177
cascading() noexcept
Definition cascading.h:46
void * allocate(size_type n, const source_location source=KTL_SOURCE()) noexcept(std::is_nothrow_default_constructible_v< node > &&detail::has_nothrow_allocate_v< Alloc > &&(!detail::has_max_size_v< Alloc >||detail::has_nothrow_max_size_v< Alloc >))
Attempts to allocate a chunk of memory defined by n.
Definition cascading.h:94
~cascading()
Definition cascading.h:58
bool operator!=(const cascading &rhs) const noexcept
Definition cascading.h:82
cascading(cascading &&other) noexcept(std::is_nothrow_move_constructible_v< node >)
Definition cascading.h:51
std::enable_if< detail::has_destroy_v< Alloc, T * >, void >::type destroy(T *p) noexcept(detail::has_nothrow_owns_v< Alloc > &&detail::has_nothrow_destroy_v< Alloc, T * >)
Destructs an object of T at the given location.
Definition cascading.h:206
cascading & operator=(cascading &&rhs) noexcept(std::is_nothrow_move_assignable_v< node >)
Definition cascading.h:65
#define KTL_EMPTY_BASE
Definition empty_base.h:6
typename get_size_type< Alloc, void >::type get_size_type_t
Definition meta.h:33
constexpr bool has_nothrow_construct_v
Definition meta.h:123
constexpr bool has_construct_v
Definition meta.h:53
void * allocate(Alloc &alloc, size_t n, const source_location source) noexcept(false)
Definition meta.h:161
void aligned_delete(T *p) noexcept(noexcept(p->~T()))
Definition aligned_malloc.h:99
constexpr bool has_no_value_type_v
Definition meta.h:17
constexpr size_t ALIGNMENT
Definition alignment.h:7
Definition cascading.h:16
#define KTL_SOURCE()
Definition source_location.h:11
Definition source_location.h:19