2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* string_name.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2019-02-12 13:30:56 +01:00
# include "string_name.h"
2017-01-16 08:04:19 +01:00
2025-04-28 17:41:48 +02:00
# include "core/os/mutex.h"
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
2020-11-07 19:33:38 -03:00
# include "core/string/print_string.h"
2017-01-16 08:04:19 +01:00
2025-04-25 19:22:29 +02:00
struct StringName : : Table {
constexpr static uint32_t TABLE_BITS = 16 ;
constexpr static uint32_t TABLE_LEN = 1 < < TABLE_BITS ;
constexpr static uint32_t TABLE_MASK = TABLE_LEN - 1 ;
2024-05-30 13:24:48 -07:00
2025-04-25 19:22:29 +02:00
static inline _Data * table [ TABLE_LEN ] ;
2025-04-09 14:53:16 +02:00
static inline BinaryMutex mutex ;
2025-04-25 19:19:03 +02:00
static inline PagedAllocator < _Data > allocator ;
2025-04-25 19:22:29 +02:00
} ;
2024-05-30 13:24:48 -07:00
2014-02-09 22:10:30 -03:00
void StringName : : setup ( ) {
ERR_FAIL_COND ( configured ) ;
2025-04-25 19:22:29 +02:00
for ( uint32_t i = 0 ; i < Table : : TABLE_LEN ; i + + ) {
Table : : table [ i ] = nullptr ;
2014-02-09 22:10:30 -03:00
}
2017-03-05 16:44:50 +01:00
configured = true ;
2014-02-09 22:10:30 -03:00
}
void StringName : : cleanup ( ) {
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
2017-01-07 18:25:37 -03:00
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
Vector < _Data * > data ;
2025-04-25 19:22:29 +02:00
for ( uint32_t i = 0 ; i < Table : : TABLE_LEN ; i + + ) {
_Data * d = Table : : table [ i ] ;
2021-07-20 17:01:18 -03:00
while ( d ) {
data . push_back ( d ) ;
d = d - > next ;
}
}
2022-03-14 00:44:19 +01:00
print_line ( " \n StringName reference ranking (from most to least referenced): \n " ) ;
2021-07-20 17:01:18 -03:00
data . sort_custom < DebugSortReferences > ( ) ;
2022-03-14 00:44:19 +01:00
int unreferenced_stringnames = 0 ;
int rarely_referenced_stringnames = 0 ;
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
2025-04-25 19:22:29 +02:00
print_line ( itos ( i + 1 ) + " : " + data [ i ] - > name + " - " + itos ( data [ i ] - > debug_references ) ) ;
2022-03-14 00:44:19 +01:00
if ( data [ i ] - > debug_references = = 0 ) {
unreferenced_stringnames + = 1 ;
} else if ( data [ i ] - > debug_references < 5 ) {
rarely_referenced_stringnames + = 1 ;
}
2021-07-20 17:01:18 -03:00
}
2022-03-14 00:44:19 +01:00
print_line ( vformat ( " \n Out of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%). " , data . size ( ) , unreferenced_stringnames , unreferenced_stringnames / float ( data . size ( ) ) * 100 ) ) ;
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 ) ) ;
2021-07-20 17:01:18 -03:00
}
# endif
2017-03-05 16:44:50 +01:00
int lost_strings = 0 ;
2025-04-25 19:22:29 +02:00
for ( uint32_t i = 0 ; i < Table : : TABLE_LEN ; i + + ) {
while ( Table : : table [ i ] ) {
_Data * d = Table : : table [ i ] ;
2022-01-20 22:09:03 +01:00
if ( d - > static_count . get ( ) ! = d - > refcount . get ( ) ) {
lost_strings + + ;
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
2025-04-03 22:09:35 +02:00
print_line ( vformat ( " Orphan StringName: %s (static: %d, total: %d) " , d - > name , d - > static_count . get ( ) , d - > refcount . get ( ) ) ) ;
2015-04-20 19:38:02 -03:00
}
}
2025-04-25 19:22:29 +02:00
Table : : table [ i ] = Table : : table [ i ] - > next ;
2025-04-25 19:19:03 +02:00
Table : : allocator . free ( d ) ;
2014-02-09 22:10:30 -03:00
}
}
2018-08-24 08:47:34 +02:00
if ( lost_strings ) {
2023-10-19 17:11:42 +02:00
print_verbose ( vformat ( " StringName: %d unclaimed string names at exit. " , lost_strings ) ) ;
2015-04-20 19:38:02 -03:00
}
2021-07-17 18:22:52 -03:00
configured = false ;
2014-02-09 22:10:30 -03:00
}
void StringName : : unref ( ) {
ERR_FAIL_COND ( ! configured ) ;
if ( _data & & _data - > refcount . unref ( ) ) {
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
2014-02-09 22:10:30 -03:00
2023-11-06 12:17:14 +02:00
if ( CoreGlobals : : leak_reporting_enabled & & _data - > static_count . get ( ) > 0 ) {
2025-04-03 22:09:35 +02:00
ERR_PRINT ( " BUG: Unreferenced static string to 0: " + _data - > name ) ;
2021-07-17 18:22:52 -03:00
}
2014-02-09 22:10:30 -03:00
if ( _data - > prev ) {
2017-03-05 16:44:50 +01:00
_data - > prev - > next = _data - > next ;
2014-02-09 22:10:30 -03:00
} else {
2025-04-25 19:22:29 +02:00
const uint32_t idx = _data - > hash & Table : : TABLE_MASK ;
Table : : table [ idx ] = _data - > next ;
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
if ( _data - > next ) {
2017-03-05 16:44:50 +01:00
_data - > next - > prev = _data - > prev ;
2014-02-09 22:10:30 -03:00
}
2025-04-25 19:19:03 +02:00
Table : : allocator . free ( _data ) ;
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2020-04-02 01:20:12 +02:00
_data = nullptr ;
2014-02-09 22:10:30 -03:00
}
2024-09-04 20:11:06 -07:00
uint32_t StringName : : get_empty_hash ( ) {
static uint32_t empty_hash = String : : hash ( " " ) ;
return empty_hash ;
}
2017-03-05 16:44:50 +01:00
bool StringName : : operator = = ( const String & p_name ) const {
2024-05-30 13:24:48 -07:00
if ( _data ) {
2025-04-25 19:22:29 +02:00
return _data - > name = = p_name ;
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2024-05-30 13:24:48 -07:00
return p_name . is_empty ( ) ;
2014-02-09 22:10:30 -03:00
}
2017-03-05 16:44:50 +01:00
bool StringName : : operator = = ( const char * p_name ) const {
2024-05-30 13:24:48 -07:00
if ( _data ) {
2025-04-25 19:22:29 +02:00
return _data - > name = = p_name ;
2014-02-09 22:10:30 -03:00
}
2024-05-30 13:24:48 -07:00
return p_name [ 0 ] = = 0 ;
2014-02-09 22:10:30 -03:00
}
2017-03-05 16:44:50 +01:00
bool StringName : : operator ! = ( const String & p_name ) const {
2014-02-09 22:10:30 -03:00
return ! ( operator = = ( p_name ) ) ;
}
2022-12-20 17:22:44 +03:00
bool StringName : : operator ! = ( const char * p_name ) const {
return ! ( operator = = ( p_name ) ) ;
}
2024-05-30 13:24:48 -07:00
char32_t StringName : : operator [ ] ( int p_index ) const {
if ( _data ) {
2025-04-03 22:09:35 +02:00
return _data - > name [ p_index ] ;
2024-05-30 13:24:48 -07:00
}
CRASH_BAD_INDEX ( p_index , 0 ) ;
return 0 ;
}
int StringName : : length ( ) const {
if ( _data ) {
2025-04-03 22:09:35 +02:00
return _data - > name . length ( ) ;
2024-05-30 13:24:48 -07:00
}
return 0 ;
}
StringName & StringName : : operator = ( const StringName & p_name ) {
2020-05-14 16:41:43 +02:00
if ( this = = & p_name ) {
2024-05-30 13:24:48 -07:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
unref ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
if ( p_name . _data & & p_name . _data - > refcount . ref ( ) ) {
2016-03-09 00:00:52 +01:00
_data = p_name . _data ;
2014-02-09 22:10:30 -03:00
}
2024-05-30 13:24:48 -07:00
return * this ;
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
StringName : : StringName ( const StringName & p_name ) {
2020-04-02 01:20:12 +02:00
_data = nullptr ;
2016-03-09 00:00:52 +01:00
2018-04-18 22:20:39 +02:00
ERR_FAIL_COND ( ! configured ) ;
if ( p_name . _data & & p_name . _data - > refcount . ref ( ) ) {
2016-03-09 00:00:52 +01:00
_data = p_name . _data ;
}
2014-02-09 22:10:30 -03:00
}
2021-07-17 18:22:52 -03:00
StringName : : StringName ( const char * p_name , bool p_static ) {
2020-04-02 01:20:12 +02:00
_data = nullptr ;
2014-02-09 22:10:30 -03:00
ERR_FAIL_COND ( ! configured ) ;
2020-05-14 16:41:43 +02:00
if ( ! p_name | | p_name [ 0 ] = = 0 ) {
2016-08-22 01:14:08 -03:00
return ; //empty, ignore
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2025-01-15 21:20:02 +01:00
const uint32_t hash = String : : hash ( p_name ) ;
2025-04-25 19:22:29 +02:00
const uint32_t idx = hash & Table : : TABLE_MASK ;
2016-03-09 00:00:52 +01:00
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
_data = Table : : table [ idx ] ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
while ( _data ) {
2014-02-09 22:10:30 -03:00
// compare hash first
2025-04-25 19:22:29 +02:00
if ( _data - > hash = = hash & & _data - > name = = p_name ) {
2014-02-09 22:10:30 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
_data = _data - > next ;
2014-02-09 22:10:30 -03:00
}
2023-02-03 19:19:52 -08:00
if ( _data & & _data - > refcount . ref ( ) ) {
// exists
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
2023-02-03 19:19:52 -08:00
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
2014-02-09 22:10:30 -03:00
}
2023-02-03 19:19:52 -08:00
# endif
2021-07-20 17:01:18 -03:00
return ;
2014-02-09 22:10:30 -03:00
}
2025-04-25 19:19:03 +02:00
_data = Table : : allocator . alloc ( ) ;
2017-03-05 16:44:50 +01:00
_data - > name = p_name ;
2014-02-09 22:10:30 -03:00
_data - > refcount . init ( ) ;
2021-07-17 18:22:52 -03:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2017-03-05 16:44:50 +01:00
_data - > hash = hash ;
2025-04-25 19:22:29 +02:00
_data - > next = Table : : table [ idx ] ;
2020-04-02 01:20:12 +02:00
_data - > prev = nullptr ;
2022-04-16 12:23:32 +02:00
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2025-04-25 19:22:29 +02:00
if ( Table : : table [ idx ] ) {
Table : : table [ idx ] - > prev = _data ;
2020-05-14 16:41:43 +02:00
}
2025-04-25 19:22:29 +02:00
Table : : table [ idx ] = _data ;
2014-02-09 22:10:30 -03:00
}
2021-07-17 18:22:52 -03:00
StringName : : StringName ( const String & p_name , bool p_static ) {
2020-04-02 01:20:12 +02:00
_data = nullptr ;
2014-02-09 22:10:30 -03:00
ERR_FAIL_COND ( ! configured ) ;
2021-12-09 03:42:46 -06:00
if ( p_name . is_empty ( ) ) {
2016-08-22 01:14:08 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2016-08-22 01:14:08 -03:00
2025-01-15 21:20:02 +01:00
const uint32_t hash = p_name . hash ( ) ;
2025-04-25 19:22:29 +02:00
const uint32_t idx = hash & Table : : TABLE_MASK ;
2014-02-09 22:10:30 -03:00
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
_data = Table : : table [ idx ] ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
while ( _data ) {
2025-04-25 19:22:29 +02:00
if ( _data - > hash = = hash & & _data - > name = = p_name ) {
2014-02-09 22:10:30 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
_data = _data - > next ;
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2023-02-03 19:19:52 -08:00
if ( _data & & _data - > refcount . ref ( ) ) {
// exists
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
2023-02-03 19:19:52 -08:00
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
2014-02-09 22:10:30 -03:00
}
2023-02-03 19:19:52 -08:00
# endif
return ;
2014-02-09 22:10:30 -03:00
}
2025-04-25 19:19:03 +02:00
_data = Table : : allocator . alloc ( ) ;
2017-03-05 16:44:50 +01:00
_data - > name = p_name ;
2014-02-09 22:10:30 -03:00
_data - > refcount . init ( ) ;
2021-07-17 18:22:52 -03:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2017-03-05 16:44:50 +01:00
_data - > hash = hash ;
2025-04-25 19:22:29 +02:00
_data - > next = Table : : table [ idx ] ;
2020-04-02 01:20:12 +02:00
_data - > prev = nullptr ;
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2025-04-25 19:22:29 +02:00
if ( Table : : table [ idx ] ) {
Table : : table [ idx ] - > prev = _data ;
2020-05-14 16:41:43 +02:00
}
2025-04-25 19:22:29 +02:00
Table : : table [ idx ] = _data ;
2014-02-09 22:10:30 -03:00
}
StringName StringName : : search ( const char * p_name ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! configured , StringName ( ) ) ;
2014-02-09 22:10:30 -03:00
2023-09-09 16:11:33 +02:00
ERR_FAIL_NULL_V ( p_name , StringName ( ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! p_name [ 0 ] ) {
2014-02-09 22:10:30 -03:00
return StringName ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-01-15 21:20:02 +01:00
const uint32_t hash = String : : hash ( p_name ) ;
2025-04-25 19:22:29 +02:00
const uint32_t idx = hash & Table : : TABLE_MASK ;
2014-02-09 22:10:30 -03:00
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
_Data * _data = Table : : table [ idx ] ;
2014-02-09 22:10:30 -03:00
2017-03-05 16:44:50 +01:00
while ( _data ) {
2014-02-09 22:10:30 -03:00
// compare hash first
2025-04-25 19:22:29 +02:00
if ( _data - > hash = = hash & & _data - > name = = p_name ) {
2014-02-09 22:10:30 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
_data = _data - > next ;
2014-02-09 22:10:30 -03:00
}
if ( _data & & _data - > refcount . ref ( ) ) {
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2014-02-09 22:10:30 -03:00
return StringName ( _data ) ;
}
return StringName ( ) ; //does not exist
}
2020-07-27 13:43:20 +03:00
StringName StringName : : search ( const char32_t * p_name ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! configured , StringName ( ) ) ;
2014-02-09 22:10:30 -03:00
2023-09-09 16:11:33 +02:00
ERR_FAIL_NULL_V ( p_name , StringName ( ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! p_name [ 0 ] ) {
2014-02-09 22:10:30 -03:00
return StringName ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-01-15 21:20:02 +01:00
const uint32_t hash = String : : hash ( p_name ) ;
2025-04-25 19:22:29 +02:00
const uint32_t idx = hash & Table : : TABLE_MASK ;
2014-02-09 22:10:30 -03:00
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
_Data * _data = Table : : table [ idx ] ;
2014-02-09 22:10:30 -03:00
2017-03-05 16:44:50 +01:00
while ( _data ) {
2014-02-09 22:10:30 -03:00
// compare hash first
2025-04-25 19:22:29 +02:00
if ( _data - > hash = = hash & & _data - > name = = p_name ) {
2014-02-09 22:10:30 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
_data = _data - > next ;
2014-02-09 22:10:30 -03:00
}
if ( _data & & _data - > refcount . ref ( ) ) {
return StringName ( _data ) ;
}
return StringName ( ) ; //does not exist
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
StringName StringName : : search ( const String & p_name ) {
2021-12-09 03:42:46 -06:00
ERR_FAIL_COND_V ( p_name . is_empty ( ) , StringName ( ) ) ;
2014-02-09 22:10:30 -03:00
2025-01-15 21:20:02 +01:00
const uint32_t hash = p_name . hash ( ) ;
2025-04-25 19:22:29 +02:00
const uint32_t idx = hash & Table : : TABLE_MASK ;
2014-02-09 22:10:30 -03:00
2025-04-25 19:22:29 +02:00
MutexLock lock ( Table : : mutex ) ;
_Data * _data = Table : : table [ idx ] ;
2014-02-09 22:10:30 -03:00
2017-03-05 16:44:50 +01:00
while ( _data ) {
2014-02-09 22:10:30 -03:00
// compare hash first
2025-04-25 19:22:29 +02:00
if ( _data - > hash = = hash & & _data - > name = = p_name ) {
2014-02-09 22:10:30 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
_data = _data - > next ;
2014-02-09 22:10:30 -03:00
}
if ( _data & & _data - > refcount . ref ( ) ) {
2021-07-20 17:01:18 -03:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2014-02-09 22:10:30 -03:00
return StringName ( _data ) ;
}
return StringName ( ) ; //does not exist
}
2020-11-04 23:01:55 -03:00
bool operator = = ( const String & p_name , const StringName & p_string_name ) {
2024-05-30 13:24:48 -07:00
return p_string_name . operator = = ( p_name ) ;
2020-11-04 23:01:55 -03:00
}
bool operator ! = ( const String & p_name , const StringName & p_string_name ) {
2024-05-30 13:24:48 -07:00
return p_string_name . operator ! = ( p_name ) ;
2020-11-04 23:01:55 -03:00
}
bool operator = = ( const char * p_name , const StringName & p_string_name ) {
2024-05-30 13:24:48 -07:00
return p_string_name . operator = = ( p_name ) ;
2020-11-04 23:01:55 -03:00
}
bool operator ! = ( const char * p_name , const StringName & p_string_name ) {
2024-05-30 13:24:48 -07:00
return p_string_name . operator ! = ( p_name ) ;
2020-11-04 23:01:55 -03:00
}