Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/string/string_name.cpp
9903 views
1
/**************************************************************************/
2
/* string_name.cpp */
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
#include "string_name.h"
32
33
#include "core/os/mutex.h"
34
#include "core/os/os.h"
35
#include "core/string/print_string.h"
36
37
struct StringName::Table {
38
constexpr static uint32_t TABLE_BITS = 16;
39
constexpr static uint32_t TABLE_LEN = 1 << TABLE_BITS;
40
constexpr static uint32_t TABLE_MASK = TABLE_LEN - 1;
41
42
static inline _Data *table[TABLE_LEN];
43
static inline BinaryMutex mutex;
44
static inline PagedAllocator<_Data> allocator;
45
};
46
47
void StringName::setup() {
48
ERR_FAIL_COND(configured);
49
for (uint32_t i = 0; i < Table::TABLE_LEN; i++) {
50
Table::table[i] = nullptr;
51
}
52
configured = true;
53
}
54
55
void StringName::cleanup() {
56
MutexLock lock(Table::mutex);
57
58
#ifdef DEBUG_ENABLED
59
if (unlikely(debug_stringname)) {
60
Vector<_Data *> data;
61
for (uint32_t i = 0; i < Table::TABLE_LEN; i++) {
62
_Data *d = Table::table[i];
63
while (d) {
64
data.push_back(d);
65
d = d->next;
66
}
67
}
68
69
print_line("\nStringName reference ranking (from most to least referenced):\n");
70
71
data.sort_custom<DebugSortReferences>();
72
int unreferenced_stringnames = 0;
73
int rarely_referenced_stringnames = 0;
74
for (int i = 0; i < data.size(); i++) {
75
print_line(itos(i + 1) + ": " + data[i]->name + " - " + itos(data[i]->debug_references));
76
if (data[i]->debug_references == 0) {
77
unreferenced_stringnames += 1;
78
} else if (data[i]->debug_references < 5) {
79
rarely_referenced_stringnames += 1;
80
}
81
}
82
83
print_line(vformat("\nOut of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%).", data.size(), unreferenced_stringnames, unreferenced_stringnames / float(data.size()) * 100));
84
print_line(vformat("Out of %d StringNames, %d StringNames were rarely referenced during this run (1-4 times) (%.2f%%).", data.size(), rarely_referenced_stringnames, rarely_referenced_stringnames / float(data.size()) * 100));
85
}
86
#endif
87
int lost_strings = 0;
88
for (uint32_t i = 0; i < Table::TABLE_LEN; i++) {
89
while (Table::table[i]) {
90
_Data *d = Table::table[i];
91
if (d->static_count.get() != d->refcount.get()) {
92
lost_strings++;
93
94
if (OS::get_singleton()->is_stdout_verbose()) {
95
print_line(vformat("Orphan StringName: %s (static: %d, total: %d)", d->name, d->static_count.get(), d->refcount.get()));
96
}
97
}
98
99
Table::table[i] = Table::table[i]->next;
100
Table::allocator.free(d);
101
}
102
}
103
if (lost_strings) {
104
print_verbose(vformat("StringName: %d unclaimed string names at exit.", lost_strings));
105
}
106
configured = false;
107
}
108
109
void StringName::unref() {
110
ERR_FAIL_COND(!configured);
111
112
if (_data && _data->refcount.unref()) {
113
MutexLock lock(Table::mutex);
114
115
if (CoreGlobals::leak_reporting_enabled && _data->static_count.get() > 0) {
116
ERR_PRINT("BUG: Unreferenced static string to 0: " + _data->name);
117
}
118
if (_data->prev) {
119
_data->prev->next = _data->next;
120
} else {
121
const uint32_t idx = _data->hash & Table::TABLE_MASK;
122
Table::table[idx] = _data->next;
123
}
124
125
if (_data->next) {
126
_data->next->prev = _data->prev;
127
}
128
Table::allocator.free(_data);
129
}
130
131
_data = nullptr;
132
}
133
134
uint32_t StringName::get_empty_hash() {
135
static uint32_t empty_hash = String::hash("");
136
return empty_hash;
137
}
138
139
bool StringName::operator==(const String &p_name) const {
140
if (_data) {
141
return _data->name == p_name;
142
}
143
144
return p_name.is_empty();
145
}
146
147
bool StringName::operator==(const char *p_name) const {
148
if (_data) {
149
return _data->name == p_name;
150
}
151
152
return p_name[0] == 0;
153
}
154
155
bool StringName::operator!=(const String &p_name) const {
156
return !(operator==(p_name));
157
}
158
159
bool StringName::operator!=(const char *p_name) const {
160
return !(operator==(p_name));
161
}
162
163
char32_t StringName::operator[](int p_index) const {
164
if (_data) {
165
return _data->name[p_index];
166
}
167
168
CRASH_BAD_INDEX(p_index, 0);
169
return 0;
170
}
171
172
int StringName::length() const {
173
if (_data) {
174
return _data->name.length();
175
}
176
177
return 0;
178
}
179
180
StringName &StringName::operator=(const StringName &p_name) {
181
if (this == &p_name) {
182
return *this;
183
}
184
185
unref();
186
187
if (p_name._data && p_name._data->refcount.ref()) {
188
_data = p_name._data;
189
}
190
191
return *this;
192
}
193
194
StringName::StringName(const StringName &p_name) {
195
_data = nullptr;
196
197
ERR_FAIL_COND(!configured);
198
199
if (p_name._data && p_name._data->refcount.ref()) {
200
_data = p_name._data;
201
}
202
}
203
204
StringName::StringName(const char *p_name, bool p_static) {
205
_data = nullptr;
206
207
ERR_FAIL_COND(!configured);
208
209
if (!p_name || p_name[0] == 0) {
210
return; //empty, ignore
211
}
212
213
const uint32_t hash = String::hash(p_name);
214
const uint32_t idx = hash & Table::TABLE_MASK;
215
216
MutexLock lock(Table::mutex);
217
_data = Table::table[idx];
218
219
while (_data) {
220
// compare hash first
221
if (_data->hash == hash && _data->name == p_name) {
222
break;
223
}
224
_data = _data->next;
225
}
226
227
if (_data && _data->refcount.ref()) {
228
// exists
229
if (p_static) {
230
_data->static_count.increment();
231
}
232
#ifdef DEBUG_ENABLED
233
if (unlikely(debug_stringname)) {
234
_data->debug_references++;
235
}
236
#endif
237
return;
238
}
239
240
_data = Table::allocator.alloc();
241
_data->name = p_name;
242
_data->refcount.init();
243
_data->static_count.set(p_static ? 1 : 0);
244
_data->hash = hash;
245
_data->next = Table::table[idx];
246
_data->prev = nullptr;
247
248
#ifdef DEBUG_ENABLED
249
if (unlikely(debug_stringname)) {
250
// Keep in memory, force static.
251
_data->refcount.ref();
252
_data->static_count.increment();
253
}
254
#endif
255
if (Table::table[idx]) {
256
Table::table[idx]->prev = _data;
257
}
258
Table::table[idx] = _data;
259
}
260
261
StringName::StringName(const String &p_name, bool p_static) {
262
_data = nullptr;
263
264
ERR_FAIL_COND(!configured);
265
266
if (p_name.is_empty()) {
267
return;
268
}
269
270
const uint32_t hash = p_name.hash();
271
const uint32_t idx = hash & Table::TABLE_MASK;
272
273
MutexLock lock(Table::mutex);
274
_data = Table::table[idx];
275
276
while (_data) {
277
if (_data->hash == hash && _data->name == p_name) {
278
break;
279
}
280
_data = _data->next;
281
}
282
283
if (_data && _data->refcount.ref()) {
284
// exists
285
if (p_static) {
286
_data->static_count.increment();
287
}
288
#ifdef DEBUG_ENABLED
289
if (unlikely(debug_stringname)) {
290
_data->debug_references++;
291
}
292
#endif
293
return;
294
}
295
296
_data = Table::allocator.alloc();
297
_data->name = p_name;
298
_data->refcount.init();
299
_data->static_count.set(p_static ? 1 : 0);
300
_data->hash = hash;
301
_data->next = Table::table[idx];
302
_data->prev = nullptr;
303
#ifdef DEBUG_ENABLED
304
if (unlikely(debug_stringname)) {
305
// Keep in memory, force static.
306
_data->refcount.ref();
307
_data->static_count.increment();
308
}
309
#endif
310
311
if (Table::table[idx]) {
312
Table::table[idx]->prev = _data;
313
}
314
Table::table[idx] = _data;
315
}
316
317
bool operator==(const String &p_name, const StringName &p_string_name) {
318
return p_string_name.operator==(p_name);
319
}
320
bool operator!=(const String &p_name, const StringName &p_string_name) {
321
return p_string_name.operator!=(p_name);
322
}
323
324
bool operator==(const char *p_name, const StringName &p_string_name) {
325
return p_string_name.operator==(p_name);
326
}
327
bool operator!=(const char *p_name, const StringName &p_string_name) {
328
return p_string_name.operator!=(p_name);
329
}
330
331