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