Diligent Engine  v.2.4.g
FixedLinearAllocator.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2021 Diligent Graphics LLC
3  * Copyright 2015-2019 Egor Yusov
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * In no event and under no legal theory, whether in tort (including negligence),
18  * contract, or otherwise, unless required by applicable law (such as deliberate
19  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
20  * liable for any damages, including any direct, indirect, special, incidental,
21  * or consequential damages of any character arising as a result of this License or
22  * out of the use or inability to use the software (including but not limited to damages
23  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
24  * all other commercial damages or losses), even if such Contributor has been advised
25  * of the possibility of such damages.
26  */
27 
28 #pragma once
29 
32 
33 #include <vector>
34 
35 #include "../../Primitives/interface/BasicTypes.h"
36 #include "../../Primitives/interface/MemoryAllocator.h"
37 #include "../../Platforms/Basic/interface/DebugUtilities.hpp"
38 #include "CompilerDefinitions.h"
39 #include "Align.hpp"
40 
41 namespace Diligent
42 {
43 
46 {
47 public:
48  // clang-format off
52  // clang-format on
53 
54  explicit FixedLinearAllocator(IMemoryAllocator& Allocator) noexcept :
55  m_pAllocator{&Allocator}
56  {}
57 
59  // clang-format off
60  m_pDataStart {Other.m_pDataStart },
61  m_pCurrPtr {Other.m_pCurrPtr },
62  m_ReservedSize {Other.m_ReservedSize },
63  m_CurrAlignment{Other.m_CurrAlignment},
64  m_pAllocator {Other.m_pAllocator }
65 #if DILIGENT_DEBUG
66  , m_DbgCurrAllocation{Other.m_DbgCurrAllocation}
67  , m_DbgAllocations{std::move(Other.m_DbgAllocations)}
68 #endif
69  // clang-format on
70  {
71  Other.Reset();
72  }
73 
75  {
76  Free();
77  }
78 
79  void Free()
80  {
81  if (m_pDataStart != nullptr && m_pAllocator != nullptr)
82  {
83  m_pAllocator->Free(m_pDataStart);
84  }
85  Reset();
86  }
87 
88  NODISCARD void* Release()
89  {
90  void* Ptr = m_pDataStart;
91  Reset();
92  return Ptr;
93  }
94 
95  NODISCARD void* ReleaseOwnership() noexcept
96  {
97  m_pAllocator = nullptr;
98  return GetDataPtr();
99  }
100 
101  NODISCARD void* GetDataPtr() const noexcept
102  {
103  return m_pDataStart;
104  }
105 
106  void AddSpace(size_t size, size_t alignment) noexcept
107  {
108  VERIFY(m_pDataStart == nullptr, "Memory has already been allocated");
109  VERIFY(IsPowerOfTwo(alignment), "Alignment is not a power of two!");
110 
111  if (size == 0)
112  return;
113 
114  if (m_CurrAlignment == 0)
115  {
116  VERIFY(m_ReservedSize == 0, "This is expected to be a very first time the space is added");
117  m_CurrAlignment = sizeof(void*);
118  }
119 
120  if (alignment > m_CurrAlignment)
121  {
122  // Reserve extra space that may be needed for alignment
123  m_ReservedSize += alignment - m_CurrAlignment;
124  }
125  m_CurrAlignment = alignment;
126 
127  size = AlignUp(size, alignment);
128  m_ReservedSize += size;
129 
130 #if DILIGENT_DEBUG
131  m_DbgAllocations.emplace_back(size, alignment, m_ReservedSize);
132 #endif
133  }
134 
135  template <typename T>
136  void AddSpace(size_t count = 1) noexcept
137  {
138  AddSpace(sizeof(T) * count, alignof(T));
139  }
140 
141  void AddSpaceForString(const Char* str) noexcept
142  {
143  if (str != nullptr)
144  AddSpace<Char>(strlen(str) + 1);
145  }
146 
147  void AddSpaceForString(const String& str) noexcept
148  {
149  if (!str.empty())
150  AddSpace<String::value_type>(str.length() + 1);
151  }
152 
153  void Reserve(size_t size)
154  {
155  VERIFY(m_pDataStart == nullptr, "Memory has already been allocated");
156  VERIFY(m_ReservedSize == 0, "Space has been added to the allocator and will be overriden");
157  m_ReservedSize = size;
158  Reserve();
159  }
160 
161  void Reserve()
162  {
163  VERIFY(m_pDataStart == nullptr, "Memory has already been allocated");
164  VERIFY(m_pAllocator != nullptr, "Allocator must not be null");
165  // Make sure the data size is at least sizeof(void*)-aligned
166  m_ReservedSize = AlignUp(m_ReservedSize, sizeof(void*));
167  if (m_ReservedSize > 0)
168  {
169  m_pDataStart = reinterpret_cast<uint8_t*>(m_pAllocator->Allocate(m_ReservedSize, "Raw memory for linear allocator", __FILE__, __LINE__));
170  VERIFY(m_pDataStart == AlignUp(m_pDataStart, sizeof(void*)), "Memory pointer must be at least sizeof(void*)-aligned");
171 
172  m_pCurrPtr = m_pDataStart;
173  }
174  m_CurrAlignment = sizeof(void*);
175  }
176 
177  NODISCARD void* Allocate(size_t size, size_t alignment)
178  {
179  VERIFY(size == 0 || m_pDataStart != nullptr, "Memory has not been allocated");
180  VERIFY(IsPowerOfTwo(alignment), "Alignment is not a power of two!");
181 
182  if (size == 0)
183  return nullptr;
184 
185  size = AlignUp(size, alignment);
186 
187 #if DILIGENT_DEBUG
188  VERIFY(m_DbgCurrAllocation < m_DbgAllocations.size(), "Allocation number exceed the number of allocations that were originally reserved.");
189  const auto& CurrAllocation = m_DbgAllocations[m_DbgCurrAllocation++];
190  VERIFY(CurrAllocation.size == size, "Allocation size (", size, ") does not match the initially requested size (", CurrAllocation.size, ")");
191  VERIFY(CurrAllocation.alignment == alignment, "Allocation alignment (", alignment, ") does not match the initially requested alignment (", CurrAllocation.alignment, ")");
192 #endif
193 
194  VERIFY(AlignUp(m_pCurrPtr, m_CurrAlignment) == m_pCurrPtr, "Current pointer is not aligned as expected");
195  m_pCurrPtr = AlignUp(m_pCurrPtr, alignment);
196  m_CurrAlignment = alignment;
197 
198  VERIFY(m_pCurrPtr + size <= m_pDataStart + CurrAllocation.reserved_size,
199  "Allocation size exceeds the initially reserved space. This is likely a bug.");
200 
201  auto* ptr = m_pCurrPtr;
202  m_pCurrPtr += size;
203 
204  VERIFY(m_pCurrPtr <= m_pDataStart + m_ReservedSize, "Allocation size exceeds the reserved space");
205 
206  return ptr;
207  }
208 
209  template <typename T>
210  NODISCARD T* Allocate(size_t count = 1)
211  {
212  return reinterpret_cast<T*>(Allocate(sizeof(T) * count, alignof(T)));
213  }
214 
215  template <typename T, typename... Args>
216  NODISCARD T* Construct(Args&&... args)
217  {
218  T* Ptr = Allocate<T>();
219  new (Ptr) T{std::forward<Args>(args)...};
220  return Ptr;
221  }
222 
223  template <typename T, typename... Args>
224  NODISCARD T* ConstructArray(size_t count, const Args&... args)
225  {
226  T* Ptr = Allocate<T>(count);
227  for (size_t i = 0; i < count; ++i)
228  {
229  new (Ptr + i) T{args...};
230  }
231  return Ptr;
232  }
233 
234  template <typename T>
235  NODISCARD T* Copy(const T& Src)
236  {
237  return Construct<T>(Src);
238  }
239 
240  template <typename T>
241  NODISCARD T* CopyArray(const T* Src, size_t count)
242  {
243  T* Dst = Allocate<T>(count);
244  for (size_t i = 0; i < count; ++i)
245  {
246  new (Dst + i) T{Src[i]};
247  }
248  return Dst;
249  }
250 
251  NODISCARD Char* CopyString(const char* Str)
252  {
253  if (Str == nullptr)
254  return nullptr;
255 
256  auto* Ptr = reinterpret_cast<Char*>(Allocate(strlen(Str) + 1, 1));
257  Char* Dst = Ptr;
258 
259  const auto* pDataEnd = reinterpret_cast<Char*>(m_pDataStart) + m_ReservedSize;
260  while (*Str != 0 && Dst < pDataEnd)
261  {
262  *(Dst++) = *(Str++);
263  }
264  if (Dst < pDataEnd)
265  *(Dst++) = 0;
266  else
267  UNEXPECTED("Not enough space reserved for the string");
268 
269  VERIFY_EXPR(reinterpret_cast<Char*>(m_pCurrPtr) == Dst);
270  return Ptr;
271  }
272 
273  NODISCARD Char* CopyString(const std::string& Str)
274  {
275  return CopyString(Str.c_str());
276  }
277 
278  NODISCARD size_t GetCurrentSize() const
279  {
280  VERIFY(m_pDataStart != nullptr, "Memory has not been allocated");
281  return static_cast<size_t>(m_pCurrPtr - m_pDataStart);
282  }
283 
284  NODISCARD size_t GetReservedSize() const
285  {
286  return m_ReservedSize;
287  }
288 
289 private:
290  void Reset()
291  {
292  m_pDataStart = nullptr;
293  m_pCurrPtr = nullptr;
294  m_ReservedSize = 0;
295  m_CurrAlignment = 0;
296  m_pAllocator = nullptr;
297 
298 #if DILIGENT_DEBUG
299  m_DbgCurrAllocation = 0;
300  m_DbgAllocations.clear();
301 #endif
302  }
303 
304  uint8_t* m_pDataStart = nullptr;
305  uint8_t* m_pCurrPtr = nullptr;
306  size_t m_ReservedSize = 0;
307  size_t m_CurrAlignment = 0;
308  IMemoryAllocator* m_pAllocator = nullptr;
309 
310 #if DILIGENT_DEBUG
311  size_t m_DbgCurrAllocation = 0;
312  struct DbgAllocationInfo
313  {
314  const size_t size;
315  const size_t alignment;
316  const size_t reserved_size;
317 
318  DbgAllocationInfo(size_t _size, size_t _alignment, size_t _reserved_size) :
319  size{_size},
320  alignment{_alignment},
321  reserved_size{_reserved_size}
322  {
323  }
324  };
325  std::vector<DbgAllocationInfo> m_DbgAllocations;
326 #endif
327 };
328 
329 } // namespace Diligent
Diligent::FixedLinearAllocator
Implementation of a linear allocator on a fixed-size memory page.
Definition: FixedLinearAllocator.hpp:45
Diligent::FixedLinearAllocator::Allocate
NODISCARD void * Allocate(size_t size, size_t alignment)
Definition: FixedLinearAllocator.hpp:177
Diligent::FixedLinearAllocator::Allocate
NODISCARD T * Allocate(size_t count=1)
Definition: FixedLinearAllocator.hpp:210
Diligent::FixedLinearAllocator::GetCurrentSize
NODISCARD size_t GetCurrentSize() const
Definition: FixedLinearAllocator.hpp:278
Diligent::Char
char Char
Definition: BasicTypes.h:64
Diligent::FixedLinearAllocator::FixedLinearAllocator
FixedLinearAllocator(IMemoryAllocator &Allocator) noexcept
Definition: FixedLinearAllocator.hpp:54
Align.hpp
Diligent::FixedLinearAllocator::~FixedLinearAllocator
~FixedLinearAllocator()
Definition: FixedLinearAllocator.hpp:74
UNEXPECTED
#define UNEXPECTED(...)
Definition: DebugUtilities.hpp:77
CompilerDefinitions.h
Diligent::FixedLinearAllocator::ReleaseOwnership
NODISCARD void * ReleaseOwnership() noexcept
Definition: FixedLinearAllocator.hpp:95
Diligent::FixedLinearAllocator::FixedLinearAllocator
FixedLinearAllocator(FixedLinearAllocator &&Other) noexcept
Definition: FixedLinearAllocator.hpp:58
Diligent::FixedLinearAllocator::ConstructArray
NODISCARD T * ConstructArray(size_t count, const Args &... args)
Definition: FixedLinearAllocator.hpp:224
Diligent::FixedLinearAllocator::GetReservedSize
NODISCARD size_t GetReservedSize() const
Definition: FixedLinearAllocator.hpp:284
Diligent::FixedLinearAllocator::CopyString
NODISCARD Char * CopyString(const char *Str)
Definition: FixedLinearAllocator.hpp:251
Diligent::FixedLinearAllocator::FixedLinearAllocator
FixedLinearAllocator(const FixedLinearAllocator &)=delete
Diligent::FixedLinearAllocator::Reserve
void Reserve()
Definition: FixedLinearAllocator.hpp:161
Diligent::FixedLinearAllocator::AddSpace
void AddSpace(size_t size, size_t alignment) noexcept
Definition: FixedLinearAllocator.hpp:106
Diligent::FixedLinearAllocator::Release
NODISCARD void * Release()
Definition: FixedLinearAllocator.hpp:88
Diligent::FixedLinearAllocator::CopyString
NODISCARD Char * CopyString(const std::string &Str)
Definition: FixedLinearAllocator.hpp:273
Diligent::FixedLinearAllocator::AddSpaceForString
void AddSpaceForString(const Char *str) noexcept
Definition: FixedLinearAllocator.hpp:141
Diligent::FixedLinearAllocator::Construct
NODISCARD T * Construct(Args &&... args)
Definition: FixedLinearAllocator.hpp:216
Diligent::IsPowerOfTwo
bool IsPowerOfTwo(T val)
Definition: Align.hpp:41
Diligent::FixedLinearAllocator::CopyArray
NODISCARD T * CopyArray(const T *Src, size_t count)
Definition: FixedLinearAllocator.hpp:241
Diligent::IMemoryAllocator
Base interface for a raw memory allocator.
Definition: MemoryAllocator.h:41
Diligent::FixedLinearAllocator::Free
void Free()
Definition: FixedLinearAllocator.hpp:79
Diligent::FixedLinearAllocator::GetDataPtr
NODISCARD void * GetDataPtr() const noexcept
Definition: FixedLinearAllocator.hpp:101
Diligent::String
std::basic_string< Char > String
String variable.
Definition: BasicTypes.h:66
VERIFY_EXPR
#define VERIFY_EXPR(...)
Definition: DebugUtilities.hpp:79
Diligent::FixedLinearAllocator::AddSpaceForString
void AddSpaceForString(const String &str) noexcept
Definition: FixedLinearAllocator.hpp:147
VERIFY
#define VERIFY(...)
Definition: DebugUtilities.hpp:76
Diligent::FixedLinearAllocator::Copy
NODISCARD T * Copy(const T &Src)
Definition: FixedLinearAllocator.hpp:235
Diligent::AlignUp
T2 ::type AlignUp(T1 val, T2 alignment)
Definition: Align.hpp:47
Diligent::IMemoryAllocator::Free
virtual void Free(void *Ptr)=0
Releases memory.
Diligent::IMemoryAllocator::Allocate
virtual void * Allocate(size_t Size, const Char *dbgDescription, const char *dbgFileName, const Int32 dbgLineNumber)=0
Allocates block of memory.
Diligent::FixedLinearAllocator::AddSpace
void AddSpace(size_t count=1) noexcept
Definition: FixedLinearAllocator.hpp:136
Diligent::FixedLinearAllocator::Reserve
void Reserve(size_t size)
Definition: FixedLinearAllocator.hpp:153
Diligent::FixedLinearAllocator::operator=
FixedLinearAllocator & operator=(const FixedLinearAllocator &)=delete
Diligent
The library uses Direct3D-style math:
Definition: AdvancedMath.hpp:37