KTL
freelist.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "../utility/assert.h"
4 #include "../utility/empty_base.h"
5 #include "../utility/meta.h"
6 #include "freelist_fwd.h"
7 #include "type_allocator.h"
8 
9 #include <cstddef>
10 #include <memory>
11 #include <type_traits>
12 
13 namespace ktl
14 {
23  template<size_t Min, size_t Max, typename Alloc>
24  class freelist
25  {
26  private:
27  static_assert(detail::has_no_value_type_v<Alloc>, "Building on top of typed allocators is not allowed. Use allocators without a type");
28  static_assert(Max >= sizeof(void*), "The freelist allocator requires a Max of at least the size of a pointer");
29 
30  public:
32 
33  private:
34  struct link
35  {
36  link* Next;
37  };
38 
39  public:
40  template<typename A = Alloc>
42  noexcept(std::is_nothrow_default_constructible_v<A>) :
43  m_Alloc(),
44  m_Free(nullptr) {}
45 
49  template<typename... Args,
50  typename = std::enable_if_t<
51  std::is_constructible_v<Alloc, Args...>>>
52  explicit freelist(Args&&... args)
53  noexcept(std::is_nothrow_constructible_v<Alloc, Args...>) :
54  m_Alloc(std::forward<Args>(args)...),
55  m_Free(nullptr) {}
56 
57  freelist(const freelist&) = delete;
58 
59  freelist(freelist&& other)
60  noexcept(std::is_nothrow_move_constructible_v<Alloc>) :
61  m_Alloc(std::move(other.m_Alloc)),
62  m_Free(other.m_Free)
63  {
64  // Moving raw allocators in use is undefined
65  KTL_ASSERT(m_Alloc == other.m_Alloc || other.m_Free == nullptr);
66 
67  other.m_Free = nullptr;
68  }
69 
71  {
72  release();
73  }
74 
75  freelist& operator=(const freelist&) = delete;
76 
78  noexcept(std::is_nothrow_move_assignable_v<Alloc>)
79  {
80  release();
81 
82  m_Alloc = std::move(rhs.m_Alloc);
83  m_Free = rhs.m_Free;
84 
85  // Moving raw allocators in use is undefined
86  KTL_ASSERT(m_Alloc == rhs.m_Alloc || rhs.m_Free == nullptr);
87 
88  rhs.m_Free = nullptr;
89 
90  return *this;
91  }
92 
93  bool operator==(const freelist& rhs) const
94  noexcept(detail::has_nothrow_equal_v<Alloc>)
95  {
96  return m_Alloc == rhs.m_Alloc && m_Free == rhs.m_Free;
97  }
98 
99  bool operator!=(const freelist& rhs) const
100  noexcept(detail::has_nothrow_not_equal_v<Alloc>)
101  {
102  return m_Alloc != rhs.m_Alloc || m_Free != rhs.m_Free;
103  }
104 
105 #pragma region Allocation
114  noexcept(detail::has_nothrow_allocate_v<Alloc>)
115  {
116  if (n > Min && n <= Max)
117  {
118  link* next = m_Free;
119  if (next)
120  {
121  m_Free = next->Next;
122  return next;
123  }
124 
125  return m_Alloc.allocate(Max);
126  }
127 
128  return nullptr;
129  }
130 
137  void deallocate(void* p, size_type n) noexcept
138  {
139  KTL_ASSERT(p != nullptr);
140 
141  if (n > Min && n <= Max && p)
142  {
143  link* next = reinterpret_cast<link*>(p);
144  next->Next = m_Free;
145  m_Free = next;
146  }
147  }
148 #pragma endregion
149 
150 #pragma region Construction
158  template<typename T, typename... Args>
159  typename std::enable_if<detail::has_construct_v<Alloc, T*, Args...>, void>::type
160  construct(T* p, Args&&... args)
161  noexcept(detail::has_nothrow_construct_v<Alloc, T*, Args...>)
162  {
163  m_Alloc.construct(p, std::forward<Args>(args)...);
164  }
165 
171  template<typename T>
172  typename std::enable_if<detail::has_destroy_v<Alloc, T*>, void>::type
173  destroy(T* p)
174  noexcept(detail::has_nothrow_destroy_v<Alloc, T*>)
175  {
176  m_Alloc.destroy(p);
177  }
178 #pragma endregion
179 
180 #pragma region Utility
186  template<typename A = Alloc>
187  typename std::enable_if<detail::has_max_size_v<A>, size_type>::type
188  max_size() const
189  noexcept(detail::has_nothrow_max_size_v<A>)
190  {
191  return m_Alloc.max_size();
192  }
193 
200  template<typename A = Alloc>
201  typename std::enable_if<detail::has_owns_v<A>, bool>::type
202  owns(void* p) const
203  noexcept(detail::has_nothrow_owns_v<A>)
204  {
205  return m_Alloc.owns(p);
206  }
207 #pragma endregion
208 
213  Alloc& get_allocator() noexcept
214  {
215  return m_Alloc;
216  }
217 
222  const Alloc& get_allocator() const noexcept
223  {
224  return m_Alloc;
225  }
226 
227  private:
228  void release()
229  noexcept(detail::has_nothrow_deallocate_v<Alloc>)
230  {
231  link* next = m_Free;
232  while (next)
233  {
234  link* prev = next;
235  next = next->Next;
236  m_Alloc.deallocate(prev, Max);
237  }
238  }
239 
240  private:
241  KTL_EMPTY_BASE Alloc m_Alloc;
242  link* m_Free;
243  };
244 }
#define KTL_ASSERT(x)
Definition: assert.h:17
An allocator which allocates using its underlying allocator. Only allocates if the requested size is ...
Definition: freelist.h:25
std::enable_if< detail::has_owns_v< A >, bool >::type owns(void *p) const noexcept(detail::has_nothrow_owns_v< A >)
Returns whether or not the allocator owns the given location in memory.
Definition: freelist.h:202
freelist() noexcept(std::is_nothrow_default_constructible_v< A >)
Definition: freelist.h:41
freelist(freelist &&other) noexcept(std::is_nothrow_move_constructible_v< Alloc >)
Definition: freelist.h:59
~freelist()
Definition: freelist.h:70
bool operator==(const freelist &rhs) const noexcept(detail::has_nothrow_equal_v< Alloc >)
Definition: freelist.h:93
freelist(const freelist &)=delete
freelist & operator=(freelist &&rhs) noexcept(std::is_nothrow_move_assignable_v< Alloc >)
Definition: freelist.h:77
std::enable_if< detail::has_destroy_v< Alloc, T * >, void >::type destroy(T *p) noexcept(detail::has_nothrow_destroy_v< Alloc, T * >)
Destructs an object of T at the given location.
Definition: freelist.h:173
void * allocate(size_type n) noexcept(detail::has_nothrow_allocate_v< Alloc >)
Attempts to allocate a chunk of memory defined by n. Will use previous allocations that were meant to...
Definition: freelist.h:113
freelist(Args &&... args) noexcept(std::is_nothrow_constructible_v< Alloc, Args... >)
Constructor for forwarding any arguments to the underlying allocator.
Definition: freelist.h:52
const Alloc & get_allocator() const noexcept
Returns a const reference to the underlying allocator.
Definition: freelist.h:222
std::enable_if< detail::has_construct_v< Alloc, T *, Args... >, void >::type construct(T *p, Args &&... args) noexcept(detail::has_nothrow_construct_v< Alloc, T *, Args... >)
Constructs an object of T with the given ...args at the given location.
Definition: freelist.h:160
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: freelist.h:188
bool operator!=(const freelist &rhs) const noexcept(detail::has_nothrow_not_equal_v< Alloc >)
Definition: freelist.h:99
void deallocate(void *p, size_type n) noexcept
Attempts to deallocate the memory at location p.
Definition: freelist.h:137
Alloc & get_allocator() noexcept
Returns a reference to the underlying allocator.
Definition: freelist.h:213
detail::get_size_type_t< Alloc > size_type
Definition: freelist.h:27
freelist & operator=(const freelist &)=delete
#define KTL_EMPTY_BASE
Definition: empty_base.h:6
constexpr bool has_construct_v
Definition: meta.h:41
constexpr bool has_nothrow_deallocate_v
Definition: meta.h:81
typename get_size_type< Alloc, void >::type get_size_type_t
Definition: meta.h:31
constexpr bool has_nothrow_max_size_v
Definition: meta.h:122
Definition: cascading.h:15