libdonut  2.3.2
Application framework for cross-platform game development in C++20
LinearAllocator.hpp
Go to the documentation of this file.
1 #ifndef DONUT_LINEAR_ALLOCATOR_HPP
2 #define DONUT_LINEAR_ALLOCATOR_HPP
3 
4 #include <algorithm> // std::max
5 #include <cassert> // assert
6 #include <cstddef> // std::size_t, std::byte, std::max_align_t
7 #include <memory> // std::align
8 #include <new> // std::align_val_t
9 #include <span> // std::span
10 #include <utility> // std::exchange, std::swap
11 #include <vector> // std::vector
12 
13 namespace donut {
14 
16 public:
18  : LinearMemoryResource(std::span<std::byte>{}) {}
19 
20  explicit LinearMemoryResource(std::span<std::byte> initialMemory) noexcept
21  : remainingMemoryBegin(initialMemory.data())
22  , remainingMemorySize(initialMemory.size())
23  , nextChunkSize(std::max(std::size_t{1024}, initialMemory.size() + initialMemory.size() / 2)) {}
24 
25  void* allocate(std::size_t size, std::size_t alignment) {
26  if (size == 0) {
27  [[unlikely]];
28  size = 1;
29  }
30  void* result = std::align(alignment, size, remainingMemoryBegin, remainingMemorySize);
31  if (!result) {
32  [[unlikely]];
33  const std::size_t newChunkSize = std::max(size, nextChunkSize);
34  const std::size_t newChunkAlignment = std::max(alignment, alignof(std::max_align_t));
35  if (extraMemory.capacity() < 4) {
36  extraMemory.reserve(4);
37  }
38  AlignedHeapMemoryChunk& newChunk = extraMemory.emplace_back(newChunkSize, newChunkAlignment);
39  remainingMemoryBegin = newChunk.memory;
40  remainingMemorySize = newChunkSize;
41  nextChunkSize += nextChunkSize / 2;
42  result = remainingMemoryBegin;
43  }
44  remainingMemoryBegin = static_cast<std::byte*>(remainingMemoryBegin) + size;
45  remainingMemorySize -= size;
46  return result;
47  }
48 
49  [[nodiscard]] std::size_t getRemainingCapacity() const noexcept {
50  return remainingMemorySize;
51  }
52 
53 private:
54  struct AlignedHeapMemoryChunk {
55  void* memory;
56  std::size_t alignment;
57 
58  AlignedHeapMemoryChunk(std::size_t size, std::size_t alignment)
59  : memory(operator new[](size, static_cast<std::align_val_t>(alignment)))
60  , alignment(alignment) {}
61 
62  ~AlignedHeapMemoryChunk() {
63  operator delete[](memory, static_cast<std::align_val_t>(alignment));
64  }
65 
66  AlignedHeapMemoryChunk(const AlignedHeapMemoryChunk&) = delete;
67 
68  AlignedHeapMemoryChunk(AlignedHeapMemoryChunk&& other) noexcept
69  : memory(std::exchange(other.memory, nullptr))
70  , alignment(std::exchange(other.alignment, 1)) {}
71 
72  AlignedHeapMemoryChunk& operator=(const AlignedHeapMemoryChunk&) = delete;
73 
74  AlignedHeapMemoryChunk& operator=(AlignedHeapMemoryChunk&& other) noexcept {
75  std::swap(memory, other.memory);
76  std::swap(alignment, other.alignment);
77  return *this;
78  }
79  };
80 
81  void* remainingMemoryBegin;
82  std::size_t remainingMemorySize;
83  std::size_t nextChunkSize;
84  std::vector<AlignedHeapMemoryChunk> extraMemory{};
85 };
86 
87 template <typename T>
89 public:
90  using value_type = T;
91 
92  LinearAllocator(LinearMemoryResource* memoryResource) noexcept
93  : memoryResource(memoryResource) {
94  assert(memoryResource);
95  }
96 
97  template <typename U>
98  LinearAllocator(const LinearAllocator<U>& other) noexcept
99  : memoryResource(other.memoryResource) {}
100 
101  LinearAllocator(const LinearAllocator& other) noexcept = default;
102  LinearAllocator(LinearAllocator&& other) noexcept = default;
103  LinearAllocator& operator=(const LinearAllocator& other) noexcept = default;
104  LinearAllocator& operator=(LinearAllocator&& other) noexcept = default;
105 
106  [[nodiscard]] T* allocate(std::size_t n) {
107  return static_cast<T*>(memoryResource->allocate(n * sizeof(T), alignof(T)));
108  }
109 
110  void deallocate(T*, std::size_t) noexcept {}
111 
112  template <typename U>
113  [[nodiscard]] bool operator==(const LinearAllocator<U>& other) const noexcept {
114  return memoryResource == other.memoryResource;
115  }
116 
117 private:
118  template <typename U>
119  friend class LinearAllocator;
120 
121  LinearMemoryResource* memoryResource;
122 };
123 
124 } // namespace donut
125 
126 #endif
Definition: LinearAllocator.hpp:88
bool operator==(const LinearAllocator< U > &other) const noexcept
Definition: LinearAllocator.hpp:113
void deallocate(T *, std::size_t) noexcept
Definition: LinearAllocator.hpp:110
LinearAllocator(LinearAllocator &&other) noexcept=default
LinearAllocator(const LinearAllocator< U > &other) noexcept
Definition: LinearAllocator.hpp:98
LinearAllocator & operator=(LinearAllocator &&other) noexcept=default
T * allocate(std::size_t n)
Definition: LinearAllocator.hpp:106
LinearAllocator & operator=(const LinearAllocator &other) noexcept=default
LinearAllocator(LinearMemoryResource *memoryResource) noexcept
Definition: LinearAllocator.hpp:92
T value_type
Definition: LinearAllocator.hpp:90
LinearAllocator(const LinearAllocator &other) noexcept=default
Definition: LinearAllocator.hpp:15
LinearMemoryResource(std::span< std::byte > initialMemory) noexcept
Definition: LinearAllocator.hpp:20
void * allocate(std::size_t size, std::size_t alignment)
Definition: LinearAllocator.hpp:25
LinearMemoryResource() noexcept
Definition: LinearAllocator.hpp:17
std::size_t getRemainingCapacity() const noexcept
Definition: LinearAllocator.hpp:49
void swap(Object &a, Object &b) noexcept
Definition: json.hpp:3980
Definition: Application.hpp:9