MxEngine
RandomAllocator.h
1 #pragma once
2 
3 #include <cassert>
4 #include <cstdint>
5 #include <ostream>
6 #include <memory>
7 #include <iomanip>
8 
9 #include "Core/Macro/Macro.h"
10 
11 namespace MxEngine
12 {
13  /*
14  RandomAllocator class by #Momo
15  accepts chunk of memory and its size, do NOT allocate or free memory by itself
16  allocates objects of any type, each can be freed in any order
17  object constructor is called automatically, object destructor is called on Free()
18  */
20  {
21  public:
22  using DataPointer = uint8_t*;
23  private:
30  uintptr_t AlignAddress(uintptr_t address, size_t align)
31  {
32  const size_t mask = align - 1;
33  MX_ASSERT((align & mask) == 0); // check for the power of 2
34  return (address + align - 1) & ~mask;
35  }
36 
43  uintptr_t AlignAddressWithPadding(uintptr_t address, size_t align)
44  {
45  const size_t mask = align - 1;
46  MX_ASSERT((align & mask) == 0); // check for the power of 2
47  return (address + align) & ~mask;
48  }
49 
56  uint8_t* AlignPointerWithPadding(uint8_t* ptr, size_t align)
57  {
58  return reinterpret_cast<uint8_t*>(AlignAddressWithPadding((uintptr_t)ptr, align));
59  }
60 
67  uint8_t* AlignPointer(uint8_t* ptr, size_t align)
68  {
69  return reinterpret_cast<uint8_t*>(AlignAddress((uintptr_t)ptr, align));
70  }
71 
75  struct Header
76  {
80  uintptr_t next;
81 
86  Header* GetNext()
87  {
88  return (Header*)(next & ~1);
89  }
90 
95  size_t GetSize()
96  {
97  return (uint8_t*)GetNext() - (uint8_t*)this - sizeof(Header);
98  }
99 
104  bool IsFree()
105  {
106  return !(next & 1);
107  }
108 
112  void MakeFree()
113  {
114  next &= ~1;
115  }
116 
120  void MakeBusy()
121  {
122  next |= 1;
123  }
124 
129  uint8_t* GetData()
130  {
131  return (uint8_t*)this + sizeof(Header);
132  }
133  };
134 
138  Header* first = nullptr;
142  Header* last = nullptr;
143 
151  void Split(Header* header, size_t bytes, size_t align)
152  {
153  if (header->GetSize() < bytes + 2 * sizeof(Header))
154  return; // not enough space to create new block
155 
156  align = (align < sizeof(uintptr_t)) ? sizeof(uintptr_t) : align;
157 
158  Header* next = (Header*)AlignPointer(header->GetData() + bytes, align);
159  next->next = (uintptr_t)header->GetNext();
160  next->MakeFree();
161  header->next = (uintptr_t)next;
162  }
163 
169  bool Collapse(Header* header)
170  {
171  Header* next = header->GetNext();
172  if (next != last && next->IsFree())
173  {
174  header->next = (uintptr_t)next->GetNext();
175  #if !defined(MXENGINE_DEBUG) // remove pointer entry in block to make allocator easier to debug
176  next->next = 0;
177  #endif
178  return true;
179  }
180  return false;
181  }
182 
183  public:
188 
194  void Init(DataPointer data, size_t bytes)
195  {
196  MX_ASSERT(bytes > sizeof(Header));
197  first = (Header*)data;
198  last = (Header*)(data + bytes);
199 
200  first->next = (uintptr_t)last;
201  first->MakeFree();
202  }
203 
209  RandomAllocator(DataPointer data, size_t bytes)
210  {
211  this->Init(data, bytes);
212  }
213 
218  DataPointer GetBase()
219  {
220  return (DataPointer)first;
221  }
222 
229  [[nodiscard]] DataPointer RawAlloc(size_t bytes, size_t align = 1)
230  {
231  MX_ASSERT(this->first != nullptr);
232 
233  Header* current = first;
234  while (current != last)
235  {
236  if (current->IsFree())
237  {
238  while (this->Collapse(current));
239  uint8_t* data = current->GetData();
240  size_t available = current->GetSize();
241  if (available >= bytes)
242  {
243  this->Split(current, bytes, align);
244  current->MakeBusy();
245  return data;
246  }
247  }
248  current = current->GetNext();
249  }
250  return nullptr;
251  }
252 
257  void RawFree(uint8_t* ptr)
258  {
259  MX_ASSERT(ptr > (uint8_t*)first && ptr < (uint8_t*)last);
260  Header* current = (Header*)(ptr - sizeof(Header));
261  while (this->Collapse(current));
262  current->MakeFree();
263  }
264 
270  template<typename T, typename... Args>
271  [[nodiscard]] T* Alloc(Args&&... args)
272  {
273  DataPointer ptr = RawAlloc(sizeof(T), alignof(T));
274  return new (ptr) T(std::forward<Args>(args)...);
275  }
276 
282  template<typename T, typename... Args>
283  [[nodiscard]] auto StackAlloc(Args&&... args)
284  {
285  auto deleter = [this](T* ptr) { this->Free(ptr); };
286  using SmartPtr = std::unique_ptr<T, decltype(deleter)>;
287  return SmartPtr(this->Alloc<T>(std::forward<Args>(args)...), std::move(deleter));
288  }
289 
294  template<typename T>
295  void Free(T* ptr)
296  {
297  ptr->~T();
298  RawFree(reinterpret_cast<uint8_t*>(ptr));
299  }
300 
305  void Dump(std::ostream& out) const
306  {
307  uint8_t* base = (uint8_t*)first;
308  size_t size = (uint8_t*)last - base;
309  for (size_t i = 0; i < size; i++)
310  {
311  if (i % sizeof(uintptr_t) == 0) out << ' ';
312  out << std::setfill('0') << std::setw(2);
313  out << std::hex << (int)base[i];
314  }
315  out << std::dec << "\n --- dumped " << size << " bytes --- \n";
316  }
317  };
318 }
DataPointer GetBase()
Definition: RandomAllocator.h:218
T * Alloc(Args &&... args)
Definition: RandomAllocator.h:271
void Free(T *ptr)
Definition: RandomAllocator.h:295
RandomAllocator()
Definition: RandomAllocator.h:187
void Init(DataPointer data, size_t bytes)
Definition: RandomAllocator.h:194
void Dump(std::ostream &out) const
Definition: RandomAllocator.h:305
void RawFree(uint8_t *ptr)
Definition: RandomAllocator.h:257
DataPointer RawAlloc(size_t bytes, size_t align=1)
Definition: RandomAllocator.h:229
RandomAllocator(DataPointer data, size_t bytes)
Definition: RandomAllocator.h:209
Definition: Application.cpp:49
auto StackAlloc(Args &&... args)
Definition: RandomAllocator.h:283
Definition: RandomAllocator.h:19