Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/os/memory.h
20934 views
1
/**************************************************************************/
2
/* memory.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/error/error_macros.h"
34
35
#include <new> // IWYU pragma: keep // `new` operators.
36
#include <type_traits>
37
38
namespace Memory {
39
constexpr size_t get_aligned_address(size_t p_address, size_t p_alignment) {
40
const size_t n_bytes_unaligned = p_address % p_alignment;
41
return (n_bytes_unaligned == 0) ? p_address : (p_address + p_alignment - n_bytes_unaligned);
42
}
43
44
#if defined(__MINGW32__) && !defined(__MINGW64__)
45
// Note: Using hardcoded value, since the value can end up different in different compile units on 32-bit windows
46
// due to a compiler bug (see GH-113145)
47
static constexpr size_t MAX_ALIGN = 16;
48
static_assert(MAX_ALIGN % alignof(max_align_t) == 0);
49
#else
50
static constexpr size_t MAX_ALIGN = alignof(max_align_t);
51
#endif
52
53
// Alignment: ↓ max_align_t ↓ uint64_t ↓ MAX_ALIGN
54
// ┌─────────────────┬──┬────────────────┬──┬───────────...
55
// │ uint64_t │░░│ uint64_t │░░│ T[]
56
// │ alloc size │░░│ element count │░░│ data
57
// └─────────────────┴──┴────────────────┴──┴───────────...
58
// Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET
59
60
inline constexpr size_t SIZE_OFFSET = 0;
61
inline constexpr size_t ELEMENT_OFFSET = get_aligned_address(SIZE_OFFSET + sizeof(uint64_t), alignof(uint64_t));
62
inline constexpr size_t DATA_OFFSET = get_aligned_address(ELEMENT_OFFSET + sizeof(uint64_t), MAX_ALIGN);
63
64
template <bool p_ensure_zero = false>
65
void *alloc_static(size_t p_bytes, bool p_pad_align = false);
66
_FORCE_INLINE_ static void *alloc_static_zeroed(size_t p_bytes, bool p_pad_align = false) {
67
return alloc_static<true>(p_bytes, p_pad_align);
68
}
69
void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
70
void free_static(void *p_ptr, bool p_pad_align = false);
71
72
// ↓ return value of alloc_aligned_static
73
// ┌─────────────────┬─────────┬─────────┬──────────────────┐
74
// │ padding (up to │ uint32_t│ void* │ padding (up to │
75
// │ p_alignment - 1)│ offset │ p_bytes │ p_alignment - 1) │
76
// └─────────────────┴─────────┴─────────┴──────────────────┘
77
//
78
// alloc_aligned_static will allocate p_bytes + p_alignment - 1 + sizeof(uint32_t) and
79
// then offset the pointer until alignment is satisfied.
80
//
81
// This offset is stored before the start of the returned ptr so we can retrieve the original/real
82
// start of the ptr in order to free it.
83
//
84
// The rest is wasted as padding in the beginning and end of the ptr. The sum of padding at
85
// both start and end of the block must add exactly to p_alignment - 1.
86
//
87
// p_alignment MUST be a power of 2.
88
void *alloc_aligned_static(size_t p_bytes, size_t p_alignment);
89
void *realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_prev_bytes, size_t p_alignment);
90
// Pass the ptr returned by alloc_aligned_static to free it.
91
// e.g.
92
// void *data = realloc_aligned_static( bytes, 16 );
93
// free_aligned_static( data );
94
void free_aligned_static(void *p_memory);
95
96
uint64_t get_mem_available();
97
uint64_t get_mem_usage();
98
uint64_t get_mem_max_usage();
99
}; //namespace Memory
100
101
class DefaultAllocator {
102
public:
103
_FORCE_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory, false); }
104
_FORCE_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr, false); }
105
};
106
107
// Works around an issue where memnew_placement (char *) would call the p_description version.
108
inline void *operator new(size_t p_size, char *p_dest) {
109
return operator new(p_size, (void *)p_dest);
110
}
111
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
112
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool
113
114
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
115
116
#ifdef _MSC_VER
117
// When compiling with VC++ 2017, the above declarations of placement new generate many irrelevant warnings (C4291).
118
// The purpose of the following definitions is to muffle these warnings, not to provide a usable implementation of placement delete.
119
void operator delete(void *p_mem, const char *p_description);
120
void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size));
121
void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description);
122
#endif
123
124
#define memalloc(m_size) Memory::alloc_static(m_size)
125
#define memalloc_zeroed(m_size) Memory::alloc_static_zeroed(m_size)
126
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
127
#define memfree(m_mem) Memory::free_static(m_mem)
128
129
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
130
131
template <typename T>
132
_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
133
postinitialize_handler(p_obj);
134
return p_obj;
135
}
136
137
#define memnew(m_class) _post_initialize(::new ("") m_class)
138
139
#define memnew_allocator(m_class, m_allocator) _post_initialize(::new (m_allocator::alloc) m_class)
140
#define memnew_placement(m_placement, m_class) _post_initialize(::new (m_placement) m_class)
141
142
_ALWAYS_INLINE_ bool predelete_handler(void *) {
143
return true;
144
}
145
146
template <typename T>
147
void memdelete(T *p_class) {
148
if (!predelete_handler(p_class)) {
149
return; // doesn't want to be deleted
150
}
151
if constexpr (!std::is_trivially_destructible_v<T>) {
152
p_class->~T();
153
}
154
155
Memory::free_static(p_class, false);
156
}
157
158
template <typename T, typename A>
159
void memdelete_allocator(T *p_class) {
160
if (!predelete_handler(p_class)) {
161
return; // doesn't want to be deleted
162
}
163
if constexpr (!std::is_trivially_destructible_v<T>) {
164
p_class->~T();
165
}
166
167
A::free(p_class);
168
}
169
170
#define memdelete_notnull(m_v) \
171
{ \
172
if (m_v) { \
173
memdelete(m_v); \
174
} \
175
}
176
177
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
178
179
_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) {
180
return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET);
181
}
182
183
template <typename T>
184
T *memnew_arr_template(size_t p_elements) {
185
if (p_elements == 0) {
186
return nullptr;
187
}
188
/** overloading operator new[] cannot be done , because it may not return the real allocated address (it may pad the 'element count' before the actual array). Because of that, it must be done by hand. This is the
189
same strategy used by std::vector, and the Vector class, so it should be safe.*/
190
191
size_t len = sizeof(T) * p_elements;
192
uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true);
193
T *failptr = nullptr; //get rid of a warning
194
ERR_FAIL_NULL_V(mem, failptr);
195
196
uint64_t *_elem_count_ptr = _get_element_count_ptr(mem);
197
*(_elem_count_ptr) = p_elements;
198
199
if constexpr (!std::is_trivially_constructible_v<T>) {
200
T *elems = (T *)mem;
201
202
/* call operator new */
203
for (size_t i = 0; i < p_elements; i++) {
204
::new (&elems[i]) T;
205
}
206
}
207
208
return (T *)mem;
209
}
210
211
// Fast alternative to a loop constructor pattern.
212
template <typename T>
213
_FORCE_INLINE_ void memnew_arr_placement(T *p_start, size_t p_num) {
214
if constexpr (is_zero_constructible_v<T>) {
215
// Can optimize with memset.
216
memset(static_cast<void *>(p_start), 0, p_num * sizeof(T));
217
} else {
218
// Need to use a for loop.
219
for (size_t i = 0; i < p_num; i++) {
220
memnew_placement(p_start + i, T());
221
}
222
}
223
}
224
225
// Convenient alternative to a loop copy pattern.
226
template <typename T>
227
_FORCE_INLINE_ void copy_arr_placement(T *p_dst, const T *p_src, size_t p_num) {
228
if constexpr (std::is_trivially_copyable_v<T>) {
229
memcpy((uint8_t *)p_dst, (uint8_t *)p_src, p_num * sizeof(T));
230
} else {
231
for (size_t i = 0; i < p_num; i++) {
232
memnew_placement(p_dst + i, T(p_src[i]));
233
}
234
}
235
}
236
237
// Convenient alternative to a loop destructor pattern.
238
template <typename T>
239
_FORCE_INLINE_ void destruct_arr_placement(T *p_dst, size_t p_num) {
240
if constexpr (!std::is_trivially_destructible_v<T>) {
241
for (size_t i = 0; i < p_num; i++) {
242
p_dst[i].~T();
243
}
244
}
245
}
246
247
/**
248
* Wonders of having own array functions, you can actually check the length of
249
* an allocated-with memnew_arr() array
250
*/
251
252
template <typename T>
253
size_t memarr_len(const T *p_class) {
254
uint8_t *ptr = (uint8_t *)p_class;
255
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
256
return *(_elem_count_ptr);
257
}
258
259
template <typename T>
260
void memdelete_arr(T *p_class) {
261
uint8_t *ptr = (uint8_t *)p_class;
262
263
if constexpr (!std::is_trivially_destructible_v<T>) {
264
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
265
uint64_t elem_count = *(_elem_count_ptr);
266
267
for (uint64_t i = 0; i < elem_count; i++) {
268
p_class[i].~T();
269
}
270
}
271
272
Memory::free_static(ptr, true);
273
}
274
275
struct _GlobalNil {
276
int color = 1;
277
_GlobalNil *right = nullptr;
278
_GlobalNil *left = nullptr;
279
_GlobalNil *parent = nullptr;
280
281
_GlobalNil();
282
};
283
284
struct _GlobalNilClass {
285
static _GlobalNil _nil;
286
};
287
288
template <typename T>
289
class DefaultTypedAllocator {
290
public:
291
template <typename... Args>
292
_FORCE_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
293
_FORCE_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
294
};
295
296