2014-02-10 01:10:30 +00:00
/**************************************************************************/
2019-02-12 12:30:56 +00:00
/* string_name.cpp */
2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* 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-04 23:50:27 +00:00
2019-02-12 12:30:56 +00:00
# include "string_name.h"
2017-01-16 07:04:19 +00:00
2018-09-11 16:13:45 +00:00
# include "core/os/os.h"
2020-11-07 22:33:38 +00:00
# include "core/string/print_string.h"
2017-01-16 07:04:19 +00:00
2014-02-10 01:10:30 +00:00
StaticCString StaticCString : : create ( const char * p_ptr ) {
StaticCString scs ;
scs . ptr = p_ptr ;
return scs ;
}
StringName : : _Data * StringName : : _table [ STRING_TABLE_LEN ] ;
2021-07-17 21:22:52 +00:00
StringName _scs_create ( const char * p_chr , bool p_static ) {
return ( p_chr [ 0 ] ? StringName ( StaticCString : : create ( p_chr ) , p_static ) : StringName ( ) ) ;
2014-02-10 01:10:30 +00:00
}
bool StringName : : configured = false ;
2020-02-28 10:02:20 +00:00
Mutex StringName : : mutex ;
2014-02-10 01:10:30 +00:00
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
bool StringName : : debug_stringname = false ;
# endif
2014-02-10 01:10:30 +00:00
void StringName : : setup ( ) {
ERR_FAIL_COND ( configured ) ;
for ( int i = 0 ; i < STRING_TABLE_LEN ; i + + ) {
2020-04-01 23:20:12 +00:00
_table [ i ] = nullptr ;
2014-02-10 01:10:30 +00:00
}
configured = true ;
}
void StringName : : cleanup ( ) {
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2017-01-07 21:25:37 +00:00
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
Vector < _Data * > data ;
for ( int i = 0 ; i < STRING_TABLE_LEN ; i + + ) {
_Data * d = _table [ i ] ;
while ( d ) {
data . push_back ( d ) ;
d = d - > next ;
}
}
2022-03-13 23:44:19 +00:00
print_line ( " \n StringName reference ranking (from most to least referenced): \n " ) ;
2021-07-20 20:01:18 +00:00
data . sort_custom < DebugSortReferences > ( ) ;
2022-03-13 23:44:19 +00:00
int unreferenced_stringnames = 0 ;
int rarely_referenced_stringnames = 0 ;
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
2021-07-20 20:01:18 +00:00
print_line ( itos ( i + 1 ) + " : " + data [ i ] - > get_name ( ) + " - " + itos ( data [ i ] - > debug_references ) ) ;
2022-03-13 23:44:19 +00:00
if ( data [ i ] - > debug_references = = 0 ) {
unreferenced_stringnames + = 1 ;
} else if ( data [ i ] - > debug_references < 5 ) {
rarely_referenced_stringnames + = 1 ;
}
2021-07-20 20:01:18 +00:00
}
2022-03-13 23:44:19 +00: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 20:01:18 +00:00
}
# endif
2015-04-20 22:38:02 +00:00
int lost_strings = 0 ;
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < STRING_TABLE_LEN ; i + + ) {
while ( _table [ i ] ) {
_Data * d = _table [ i ] ;
2022-01-20 21:09:03 +00:00
if ( d - > static_count . get ( ) ! = d - > refcount . get ( ) ) {
lost_strings + + ;
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
2023-10-19 15:11:42 +00:00
String dname = String ( d - > cname ? d - > cname : d - > name ) ;
print_line ( vformat ( " Orphan StringName: %s (static: %d, total: %d) " , dname , d - > static_count . get ( ) , d - > refcount . get ( ) ) ) ;
2015-04-20 22:38:02 +00:00
}
}
2016-03-08 23:00:52 +00:00
_table [ i ] = _table [ i ] - > next ;
2014-02-10 01:10:30 +00:00
memdelete ( d ) ;
}
}
2018-08-24 06:47:34 +00:00
if ( lost_strings ) {
2023-10-19 15:11:42 +00:00
print_verbose ( vformat ( " StringName: %d unclaimed string names at exit. " , lost_strings ) ) ;
2015-04-20 22:38:02 +00:00
}
2021-07-17 21:22:52 +00:00
configured = false ;
2014-02-10 01:10:30 +00:00
}
void StringName : : unref ( ) {
ERR_FAIL_COND ( ! configured ) ;
if ( _data & & _data - > refcount . unref ( ) ) {
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2014-02-10 01:10:30 +00:00
2023-11-06 10:17:14 +00:00
if ( CoreGlobals : : leak_reporting_enabled & & _data - > static_count . get ( ) > 0 ) {
2021-07-17 21:22:52 +00:00
if ( _data - > cname ) {
ERR_PRINT ( " BUG: Unreferenced static string to 0: " + String ( _data - > cname ) ) ;
} else {
ERR_PRINT ( " BUG: Unreferenced static string to 0: " + String ( _data - > name ) ) ;
}
}
2014-02-10 01:10:30 +00:00
if ( _data - > prev ) {
_data - > prev - > next = _data - > next ;
} else {
if ( _table [ _data - > idx ] ! = _data ) {
ERR_PRINT ( " BUG! " ) ;
}
_table [ _data - > idx ] = _data - > next ;
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
if ( _data - > next ) {
_data - > next - > prev = _data - > prev ;
}
memdelete ( _data ) ;
}
2016-03-08 23:00:52 +00:00
2020-04-01 23:20:12 +00:00
_data = nullptr ;
2014-02-10 01:10:30 +00:00
}
bool StringName : : operator = = ( const String & p_name ) const {
if ( ! _data ) {
return ( p_name . length ( ) = = 0 ) ;
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
return ( _data - > get_name ( ) = = p_name ) ;
}
bool StringName : : operator = = ( const char * p_name ) const {
if ( ! _data ) {
return ( p_name [ 0 ] = = 0 ) ;
}
return ( _data - > get_name ( ) = = p_name ) ;
}
bool StringName : : operator ! = ( const String & p_name ) const {
return ! ( operator = = ( p_name ) ) ;
}
2022-12-20 14:22:44 +00:00
bool StringName : : operator ! = ( const char * p_name ) const {
return ! ( operator = = ( p_name ) ) ;
}
2014-02-10 01:10:30 +00:00
bool StringName : : operator ! = ( const StringName & p_name ) const {
2016-03-08 23:00:52 +00:00
// the real magic of all this mess happens here.
2014-02-10 01:10:30 +00:00
// this is why path comparisons are very fast
return _data ! = p_name . _data ;
}
void StringName : : operator = ( const StringName & p_name ) {
2020-05-14 14:41:43 +00:00
if ( this = = & p_name ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
unref ( ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
if ( p_name . _data & & p_name . _data - > refcount . ref ( ) ) {
2016-03-08 23:00:52 +00:00
_data = p_name . _data ;
2014-02-10 01:10:30 +00:00
}
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
StringName : : StringName ( const StringName & p_name ) {
2020-04-01 23:20:12 +00:00
_data = nullptr ;
2016-03-08 23:00:52 +00:00
2018-04-18 20:20:39 +00:00
ERR_FAIL_COND ( ! configured ) ;
if ( p_name . _data & & p_name . _data - > refcount . ref ( ) ) {
2016-03-08 23:00:52 +00:00
_data = p_name . _data ;
}
2014-02-10 01:10:30 +00:00
}
2023-04-07 21:32:37 +00:00
void StringName : : assign_static_unique_class_name ( StringName * ptr , const char * p_name ) {
mutex . lock ( ) ;
if ( * ptr = = StringName ( ) ) {
* ptr = StringName ( p_name , true ) ;
}
mutex . unlock ( ) ;
}
2021-07-17 21:22:52 +00:00
StringName : : StringName ( const char * p_name , bool p_static ) {
2020-04-01 23:20:12 +00:00
_data = nullptr ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND ( ! configured ) ;
2020-05-14 14:41:43 +00:00
if ( ! p_name | | p_name [ 0 ] = = 0 ) {
2016-08-22 04:14:08 +00:00
return ; //empty, ignore
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
uint32_t hash = String : : hash ( p_name ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
uint32_t idx = hash & STRING_TABLE_MASK ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
_data = _table [ idx ] ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
while ( _data ) {
// compare hash first
2020-05-14 14:41:43 +00:00
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_data = _data - > next ;
}
2023-02-04 03:19:52 +00:00
if ( _data & & _data - > refcount . ref ( ) ) {
// exists
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
2023-02-04 03:19:52 +00:00
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
2014-02-10 01:10:30 +00:00
}
2023-02-04 03:19:52 +00:00
# endif
2021-07-20 20:01:18 +00:00
return ;
2014-02-10 01:10:30 +00:00
}
_data = memnew ( _Data ) ;
_data - > name = p_name ;
_data - > refcount . init ( ) ;
2021-07-17 21:22:52 +00:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2014-02-10 01:10:30 +00:00
_data - > hash = hash ;
_data - > idx = idx ;
2020-04-01 23:20:12 +00:00
_data - > cname = nullptr ;
2014-02-10 01:10:30 +00:00
_data - > next = _table [ idx ] ;
2020-04-01 23:20:12 +00:00
_data - > prev = nullptr ;
2022-04-16 10:23:32 +00:00
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2020-05-14 14:41:43 +00:00
if ( _table [ idx ] ) {
2014-02-10 01:10:30 +00:00
_table [ idx ] - > prev = _data ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_table [ idx ] = _data ;
}
2021-07-17 21:22:52 +00:00
StringName : : StringName ( const StaticCString & p_static_string , bool p_static ) {
2020-04-01 23:20:12 +00:00
_data = nullptr ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND ( ! configured ) ;
ERR_FAIL_COND ( ! p_static_string . ptr | | ! p_static_string . ptr [ 0 ] ) ;
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2014-02-10 01:10:30 +00:00
uint32_t hash = String : : hash ( p_static_string . ptr ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_data = _table [ idx ] ;
while ( _data ) {
// compare hash first
2020-05-14 14:41:43 +00:00
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_static_string . ptr ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_data = _data - > next ;
}
2023-02-04 03:19:52 +00:00
if ( _data & & _data - > refcount . ref ( ) ) {
// exists
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
2023-02-04 03:19:52 +00:00
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
2014-02-10 01:10:30 +00:00
}
2023-02-04 03:19:52 +00:00
# endif
return ;
2014-02-10 01:10:30 +00:00
}
_data = memnew ( _Data ) ;
_data - > refcount . init ( ) ;
2021-07-17 21:22:52 +00:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2014-02-10 01:10:30 +00:00
_data - > hash = hash ;
2016-03-08 23:00:52 +00:00
_data - > idx = idx ;
2014-02-10 01:10:30 +00:00
_data - > cname = p_static_string . ptr ;
_data - > next = _table [ idx ] ;
2020-04-01 23:20:12 +00:00
_data - > prev = nullptr ;
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2020-05-14 14:41:43 +00:00
if ( _table [ idx ] ) {
2014-02-10 01:10:30 +00:00
_table [ idx ] - > prev = _data ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_table [ idx ] = _data ;
}
2021-07-17 21:22:52 +00:00
StringName : : StringName ( const String & p_name , bool p_static ) {
2020-04-01 23:20:12 +00:00
_data = nullptr ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND ( ! configured ) ;
2021-12-09 09:42:46 +00:00
if ( p_name . is_empty ( ) ) {
2016-08-22 04:14:08 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-08-22 04:14:08 +00:00
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
uint32_t hash = p_name . hash ( ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_data = _table [ idx ] ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
while ( _data ) {
2020-05-14 14:41:43 +00:00
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_data = _data - > next ;
}
2016-03-08 23:00:52 +00:00
2023-02-04 03:19:52 +00:00
if ( _data & & _data - > refcount . ref ( ) ) {
// exists
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
2023-02-04 03:19:52 +00:00
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
2014-02-10 01:10:30 +00:00
}
2023-02-04 03:19:52 +00:00
# endif
return ;
2014-02-10 01:10:30 +00:00
}
_data = memnew ( _Data ) ;
_data - > name = p_name ;
_data - > refcount . init ( ) ;
2021-07-17 21:22:52 +00:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2014-02-10 01:10:30 +00:00
_data - > hash = hash ;
_data - > idx = idx ;
2020-04-01 23:20:12 +00:00
_data - > cname = nullptr ;
2014-02-10 01:10:30 +00:00
_data - > next = _table [ idx ] ;
2020-04-01 23:20:12 +00:00
_data - > prev = nullptr ;
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2020-05-14 14:41:43 +00:00
if ( _table [ idx ] ) {
2014-02-10 01:10:30 +00:00
_table [ idx ] - > prev = _data ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_table [ idx ] = _data ;
}
StringName StringName : : search ( const char * p_name ) {
ERR_FAIL_COND_V ( ! configured , StringName ( ) ) ;
2023-09-09 14:11:33 +00:00
ERR_FAIL_NULL_V ( p_name , StringName ( ) ) ;
2020-05-14 14:41:43 +00:00
if ( ! p_name [ 0 ] ) {
2014-02-10 01:10:30 +00:00
return StringName ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2014-02-10 01:10:30 +00:00
uint32_t hash = String : : hash ( p_name ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_Data * _data = _table [ idx ] ;
while ( _data ) {
// compare hash first
2020-05-14 14:41:43 +00:00
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_data = _data - > next ;
}
if ( _data & & _data - > refcount . ref ( ) ) {
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2014-02-10 01:10:30 +00:00
return StringName ( _data ) ;
}
return StringName ( ) ; //does not exist
}
2020-07-27 10:43:20 +00:00
StringName StringName : : search ( const char32_t * p_name ) {
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND_V ( ! configured , StringName ( ) ) ;
2023-09-09 14:11:33 +00:00
ERR_FAIL_NULL_V ( p_name , StringName ( ) ) ;
2020-05-14 14:41:43 +00:00
if ( ! p_name [ 0 ] ) {
2014-02-10 01:10:30 +00:00
return StringName ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2014-02-10 01:10:30 +00:00
uint32_t hash = String : : hash ( p_name ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_Data * _data = _table [ idx ] ;
while ( _data ) {
// compare hash first
2020-05-14 14:41:43 +00:00
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_data = _data - > next ;
}
if ( _data & & _data - > refcount . ref ( ) ) {
return StringName ( _data ) ;
}
return StringName ( ) ; //does not exist
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
StringName StringName : : search ( const String & p_name ) {
2021-12-09 09:42:46 +00:00
ERR_FAIL_COND_V ( p_name . is_empty ( ) , StringName ( ) ) ;
2014-02-10 01:10:30 +00:00
2020-02-28 10:02:20 +00:00
MutexLock lock ( mutex ) ;
2014-02-10 01:10:30 +00:00
uint32_t hash = p_name . hash ( ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_Data * _data = _table [ idx ] ;
while ( _data ) {
// compare hash first
2020-05-14 14:41:43 +00:00
if ( _data - > hash = = hash & & p_name = = _data - > get_name ( ) ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_data = _data - > next ;
}
if ( _data & & _data - > refcount . ref ( ) ) {
2021-07-20 20:01:18 +00:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2014-02-10 01:10:30 +00:00
return StringName ( _data ) ;
}
return StringName ( ) ; //does not exist
}
2020-11-05 02:01:55 +00:00
bool operator = = ( const String & p_name , const StringName & p_string_name ) {
return p_name = = p_string_name . operator String ( ) ;
}
bool operator ! = ( const String & p_name , const StringName & p_string_name ) {
return p_name ! = p_string_name . operator String ( ) ;
}
bool operator = = ( const char * p_name , const StringName & p_string_name ) {
return p_name = = p_string_name . operator String ( ) ;
}
bool operator ! = ( const char * p_name , const StringName & p_string_name ) {
return p_name ! = p_string_name . operator String ( ) ;
}