Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/os/memory.h
9903 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
#include "core/templates/safe_refcount.h"
35
36
#include <new> // IWYU pragma: keep // `new` operators.
37
#include <type_traits>
38
39
class Memory {
40
#ifdef DEBUG_ENABLED
41
static SafeNumeric<uint64_t> mem_usage;
42
static SafeNumeric<uint64_t> max_usage;
43
#endif
44
45
public:
46
// Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t
47
// ┌─────────────────┬──┬────────────────┬──┬───────────...
48
// │ uint64_t │░░│ uint64_t │░░│ T[]
49
// │ alloc size │░░│ element count │░░│ data
50
// └─────────────────┴──┴────────────────┴──┴───────────...
51
// Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET
52
53
static constexpr size_t SIZE_OFFSET = 0;
54
static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t)));
55
static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t)));
56
57
template <bool p_ensure_zero = false>
58
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
59
_FORCE_INLINE_ static void *alloc_static_zeroed(size_t p_bytes, bool p_pad_align = false) { return alloc_static<true>(p_bytes, p_pad_align); }
60
static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
61
static void free_static(void *p_ptr, bool p_pad_align = false);
62
63
// ↓ return value of alloc_aligned_static
64
// ┌─────────────────┬─────────┬─────────┬──────────────────┐
65
// │ padding (up to │ uint32_t│ void* │ padding (up to │
66
// │ p_alignment - 1)│ offset │ p_bytes │ p_alignment - 1) │
67
// └─────────────────┴─────────┴─────────┴──────────────────┘
68
//
69
// alloc_aligned_static will allocate p_bytes + p_alignment - 1 + sizeof(uint32_t) and
70
// then offset the pointer until alignment is satisfied.
71
//
72
// This offset is stored before the start of the returned ptr so we can retrieve the original/real
73
// start of the ptr in order to free it.
74
//
75
// The rest is wasted as padding in the beginning and end of the ptr. The sum of padding at
76
// both start and end of the block must add exactly to p_alignment - 1.
77
//
78
// p_alignment MUST be a power of 2.
79
static void *alloc_aligned_static(size_t p_bytes, size_t p_alignment);
80
static void *realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_prev_bytes, size_t p_alignment);
81
// Pass the ptr returned by alloc_aligned_static to free it.
82
// e.g.
83
// void *data = realloc_aligned_static( bytes, 16 );
84
// free_aligned_static( data );
85
static void free_aligned_static(void *p_memory);
86
87
static uint64_t get_mem_available();
88
static uint64_t get_mem_usage();
89
static uint64_t get_mem_max_usage();
90
};
91
92
class DefaultAllocator {
93
public:
94
_FORCE_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory, false); }
95
_FORCE_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr, false); }
96
};
97
98
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
99
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool
100
101
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
102
103
#ifdef _MSC_VER
104
// When compiling with VC++ 2017, the above declarations of placement new generate many irrelevant warnings (C4291).
105
// The purpose of the following definitions is to muffle these warnings, not to provide a usable implementation of placement delete.
106
void operator delete(void *p_mem, const char *p_description);
107
void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size));
108
void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description);
109
#endif
110
111
#define memalloc(m_size) Memory::alloc_static(m_size)
112
#define memalloc_zeroed(m_size) Memory::alloc_static_zeroed(m_size)
113
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
114
#define memfree(m_mem) Memory::free_static(m_mem)
115
116
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
117
118
template <typename T>
119
_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
120
postinitialize_handler(p_obj);
121
return p_obj;
122
}
123
124
#define memnew(m_class) _post_initialize(::new ("") m_class)
125
126
#define memnew_allocator(m_class, m_allocator) _post_initialize(::new (m_allocator::alloc) m_class)
127
#define memnew_placement(m_placement, m_class) _post_initialize(::new (m_placement) m_class)
128
129
_ALWAYS_INLINE_ bool predelete_handler(void *) {
130
return true;
131
}
132
133
template <typename T>
134
void memdelete(T *p_class) {
135
if (!predelete_handler(p_class)) {
136
return; // doesn't want to be deleted
137
}
138
if constexpr (!std::is_trivially_destructible_v<T>) {
139
p_class->~T();
140
}
141
142
Memory::free_static(p_class, false);
143
}
144
145
template <typename T, typename A>
146
void memdelete_allocator(T *p_class) {
147
if (!predelete_handler(p_class)) {
148
return; // doesn't want to be deleted
149
}
150
if constexpr (!std::is_trivially_destructible_v<T>) {
151
p_class->~T();
152
}
153
154
A::free(p_class);
155
}
156
157
#define memdelete_notnull(m_v) \
158
{ \
159
if (m_v) { \
160
memdelete(m_v); \
161
} \
162
}
163
164
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
165
166
_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) {
167
return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET);
168
}
169
170
template <typename T>
171
T *memnew_arr_template(size_t p_elements) {
172
if (p_elements == 0) {
173
return nullptr;
174
}
175
/** 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
176
same strategy used by std::vector, and the Vector class, so it should be safe.*/
177
178
size_t len = sizeof(T) * p_elements;
179
uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true);
180
T *failptr = nullptr; //get rid of a warning
181
ERR_FAIL_NULL_V(mem, failptr);
182
183
uint64_t *_elem_count_ptr = _get_element_count_ptr(mem);
184
*(_elem_count_ptr) = p_elements;
185
186
if constexpr (!std::is_trivially_constructible_v<T>) {
187
T *elems = (T *)mem;
188
189
/* call operator new */
190
for (size_t i = 0; i < p_elements; i++) {
191
::new (&elems[i]) T;
192
}
193
}
194
195
return (T *)mem;
196
}
197
198
// Fast alternative to a loop constructor pattern.
199
template <typename T>
200
_FORCE_INLINE_ void memnew_arr_placement(T *p_start, size_t p_num) {
201
if constexpr (is_zero_constructible_v<T>) {
202
// Can optimize with memset.
203
memset(static_cast<void *>(p_start), 0, p_num * sizeof(T));
204
} else {
205
// Need to use a for loop.
206
for (size_t i = 0; i < p_num; i++) {
207
memnew_placement(p_start + i, T());
208
}
209
}
210
}
211
212
/**
213
* Wonders of having own array functions, you can actually check the length of
214
* an allocated-with memnew_arr() array
215
*/
216
217
template <typename T>
218
size_t memarr_len(const T *p_class) {
219
uint8_t *ptr = (uint8_t *)p_class;
220
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
221
return *(_elem_count_ptr);
222
}
223
224
template <typename T>
225
void memdelete_arr(T *p_class) {
226
uint8_t *ptr = (uint8_t *)p_class;
227
228
if constexpr (!std::is_trivially_destructible_v<T>) {
229
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
230
uint64_t elem_count = *(_elem_count_ptr);
231
232
for (uint64_t i = 0; i < elem_count; i++) {
233
p_class[i].~T();
234
}
235
}
236
237
Memory::free_static(ptr, true);
238
}
239
240
struct _GlobalNil {
241
int color = 1;
242
_GlobalNil *right = nullptr;
243
_GlobalNil *left = nullptr;
244
_GlobalNil *parent = nullptr;
245
246
_GlobalNil();
247
};
248
249
struct _GlobalNilClass {
250
static _GlobalNil _nil;
251
};
252
253
template <typename T>
254
class DefaultTypedAllocator {
255
public:
256
template <typename... Args>
257
_FORCE_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
258
_FORCE_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
259
};
260
261