1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Prerequisites/BsPrerequisitesUtil.h"
6
7namespace bs
8{
9 /** @addtogroup Internal-Utility
10 * @{
11 */
12
13 /** @addtogroup Memory-Internal
14 * @{
15 */
16
17 /**
18 * Provides an easy way to group multiple allocations under a single (actual) allocation. Requires the user to first
19 * call reserve() methods for all requested data elements, followed by init(), after which allocation/deallocation
20 * can follow using construct/destruct or alloc/free methods.
21 */
22 class GroupAlloc : INonCopyable
23 {
24 public:
25 GroupAlloc() = default;
26
27 GroupAlloc(GroupAlloc&& other) noexcept
28 : mData(other.mData), mDataPtr(other.mDataPtr), mNumBytes(other.mNumBytes)
29 {
30 other.mData = nullptr;
31 other.mDataPtr = nullptr;
32 other.mNumBytes = 0;
33 }
34
35 ~GroupAlloc()
36 {
37 if (mNumBytes > 0)
38 bs_free(mData);
39 }
40
41 GroupAlloc& operator=(GroupAlloc&& other) noexcept
42 {
43 if (this == &other)
44 return *this;
45
46 if (mNumBytes > 0)
47 bs_free(mData);
48
49 mData = other.mData;
50 mDataPtr = other.mDataPtr;
51 mNumBytes = other.mNumBytes;
52
53 other.mData = nullptr;
54 other.mDataPtr = nullptr;
55 other.mNumBytes = 0;
56
57 return *this;
58 }
59
60 /**
61 * Allocates internal memory as reserved by previous calls to reserve(). Must be called before any calls to
62 * construct or alloc.
63 */
64 void init()
65 {
66 assert(mData == nullptr);
67
68 if (mNumBytes > 0)
69 mData = (UINT8*)bs_alloc(mNumBytes);
70
71 mDataPtr = mData;
72 }
73
74 /**
75 * Reserves the specified amount of bytes to allocate. Multiple calls to reserve() are cumulative. After all needed
76 * memory is reserved, call init(), followed by actual allocation via construct() or alloc() methods.
77 */
78 GroupAlloc& reserve(UINT32 amount)
79 {
80 assert(mData == nullptr);
81
82 mNumBytes += amount;
83 return *this;
84 }
85
86 /**
87 * Reserves the specified amount of bytes to allocate. Multiple calls to reserve() are cumulative. After all needed
88 * memory is reserved, call init(), followed by actual allocation via construct() or alloc() methods. If you need
89 * to change the size of your allocation, free your memory by using free(), followed by a call to clear(). Then
90 * reserve(), init() and alloc() again.
91 */
92 template<class T>
93 GroupAlloc& reserve(UINT32 count = 1)
94 {
95 assert(mData == nullptr);
96
97 mNumBytes += sizeof(T) * count;
98 return *this;
99 }
100
101 /**
102 * Allocates a new piece of memory of the specified size.
103 *
104 * @param[in] amount Amount of memory to allocate, in bytes.
105 */
106 UINT8* alloc(UINT32 amount)
107 {
108 assert(mDataPtr + amount <= (mData + mNumBytes));
109
110 UINT8* output = mDataPtr;
111 mDataPtr += amount;
112
113 return output;
114 }
115
116 /**
117 * Allocates enough memory to hold @p count elements of the specified type.
118 *
119 * @param[in] count Number of elements to allocate.
120 */
121 template<class T>
122 T* alloc(UINT32 count = 1)
123 {
124 return (T*)alloc(sizeof(T) * count);
125 }
126
127 /** Deallocates a previously allocated piece of memory. */
128 void free(void* data)
129 {
130 // Do nothing
131 }
132
133 /** Frees any internally allocated buffers. All elements must be previously freed by calling free(). */
134 void clear()
135 {
136 // Note: A debug check if user actually freed the memory could be helpful
137 if (mData)
138 bs_free(mData);
139
140 mNumBytes = 0;
141 mData = nullptr;
142 mDataPtr = nullptr;
143 }
144
145 /**
146 * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
147 */
148 template<class T>
149 T* construct(UINT32 count = 1)
150 {
151 T* data = (T*)alloc(sizeof(T) * count);
152
153 for(unsigned int i = 0; i < count; i++)
154 new ((void*)&data[i]) T;
155
156 return data;
157 }
158
159 /**
160 * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
161 */
162 template<class T, class... Args>
163 T* construct(Args &&...args, UINT32 count = 1)
164 {
165 T* data = (T*)alloc(sizeof(T) * count);
166
167 for(unsigned int i = 0; i < count; i++)
168 new ((void*)&data[i]) T(std::forward<Args>(args)...);
169
170 return data;
171 }
172
173 /** Destructs and deallocates an object allocated with the static allocator. */
174 template<class T>
175 void destruct(T* data)
176 {
177 data->~T();
178
179 free(data);
180 }
181
182 /** Destructs and deallocates an array of objects allocated with the static frame allocator. */
183 template<class T>
184 void destruct(T* data, UINT32 count)
185 {
186 for(unsigned int i = 0; i < count; i++)
187 data[i].~T();
188
189 free(data);
190 }
191
192 private:
193 UINT8* mData = nullptr;
194 UINT8* mDataPtr = nullptr;
195 UINT32 mNumBytes = 0;
196 };
197
198 /** @} */
199 /** @} */
200}
201