2022-03-15 13:29:32 +01:00
/*************************************************************************/
/* string_name.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
# include "string_name.h"
# include "core/os/os.h"
2022-08-17 14:19:55 +02:00
# include "core/string/print_string.h"
2022-03-15 13:29:32 +01:00
StaticCString StaticCString : : create ( const char * p_ptr ) {
StaticCString scs ;
scs . ptr = p_ptr ;
return scs ;
}
StringName : : _Data * StringName : : _table [ STRING_TABLE_LEN ] ;
2022-08-08 17:58:43 +02:00
StringName _scs_create ( const char * p_chr , bool p_static ) {
return ( p_chr [ 0 ] ? StringName ( StaticCString : : create ( p_chr ) , p_static ) : StringName ( ) ) ;
2022-03-15 13:29:32 +01:00
}
bool StringName : : configured = false ;
Mutex StringName : : lock ;
2022-08-08 17:26:23 +02:00
# ifdef DEBUG_ENABLED
bool StringName : : debug_stringname = false ;
# endif
2022-03-15 13:29:32 +01:00
void StringName : : setup ( ) {
ERR_FAIL_COND ( configured ) ;
for ( int i = 0 ; i < STRING_TABLE_LEN ; i + + ) {
_table [ i ] = nullptr ;
}
configured = true ;
}
void StringName : : cleanup ( ) {
lock . lock ( ) ;
2022-08-08 17:26:23 +02: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 ;
}
}
print_line ( " \n StringName reference ranking (from most to least referenced): \n " ) ;
data . sort_custom < DebugSortReferences > ( ) ;
int unreferenced_stringnames = 0 ;
int rarely_referenced_stringnames = 0 ;
for ( int i = 0 ; i < data . size ( ) ; i + + ) {
print_line ( itos ( i + 1 ) + " : " + data [ i ] - > get_name ( ) + " - " + itos ( data [ i ] - > debug_references ) ) ;
if ( data [ i ] - > debug_references = = 0 ) {
unreferenced_stringnames + = 1 ;
} else if ( data [ i ] - > debug_references < 5 ) {
rarely_referenced_stringnames + = 1 ;
}
}
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 ) ) ;
}
# endif
2022-03-15 13:29:32 +01:00
int lost_strings = 0 ;
for ( int i = 0 ; i < STRING_TABLE_LEN ; i + + ) {
while ( _table [ i ] ) {
_Data * d = _table [ i ] ;
2022-08-08 17:26:23 +02:00
if ( d - > static_count . get ( ) ! = d - > refcount . get ( ) ) {
lost_strings + + ;
if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
if ( d - > cname ) {
print_line ( " Orphan StringName: " + String ( d - > cname ) ) ;
} else {
print_line ( " Orphan StringName: " + String ( d - > name ) ) ;
}
2022-03-15 13:29:32 +01:00
}
}
_table [ i ] = _table [ i ] - > next ;
memdelete ( d ) ;
}
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
if ( lost_strings ) {
print_verbose ( " StringName: " + itos ( lost_strings ) + " unclaimed string names at exit. " ) ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
lock . unlock ( ) ;
}
void StringName : : unref ( ) {
ERR_FAIL_COND ( ! configured ) ;
if ( _data & & _data - > refcount . unref ( ) ) {
lock . lock ( ) ;
2022-08-08 17:26:23 +02:00
if ( _data - > static_count . get ( ) > 0 ) {
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 ) ) ;
}
}
2022-03-15 13:29:32 +01:00
if ( _data - > prev ) {
_data - > prev - > next = _data - > next ;
} else {
if ( _table [ _data - > idx ] ! = _data ) {
ERR_PRINT ( " BUG! " ) ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_table [ _data - > idx ] = _data - > next ;
}
if ( _data - > next ) {
_data - > next - > prev = _data - > prev ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
memdelete ( _data ) ;
lock . unlock ( ) ;
}
_data = nullptr ;
}
bool StringName : : operator = = ( const String & p_name ) const {
if ( ! _data ) {
return ( p_name . length ( ) = = 0 ) ;
}
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 ) ) ;
}
bool StringName : : operator ! = ( const StringName & p_name ) const {
// the real magic of all this mess happens here.
// this is why path comparisons are very fast
return _data ! = p_name . _data ;
}
void StringName : : operator = ( const StringName & p_name ) {
if ( this = = & p_name ) {
return ;
}
unref ( ) ;
if ( p_name . _data & & p_name . _data - > refcount . ref ( ) ) {
_data = p_name . _data ;
}
}
StringName : : StringName ( const StringName & p_name ) {
_data = nullptr ;
ERR_FAIL_COND ( ! configured ) ;
if ( p_name . _data & & p_name . _data - > refcount . ref ( ) ) {
_data = p_name . _data ;
}
}
2022-08-08 17:26:23 +02:00
StringName : : StringName ( const char * p_name , bool p_static ) {
2022-03-15 13:29:32 +01:00
_data = nullptr ;
ERR_FAIL_COND ( ! configured ) ;
if ( ! p_name | | p_name [ 0 ] = = 0 ) {
return ; //empty, ignore
}
lock . lock ( ) ;
uint32_t hash = String : : hash ( p_name ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_data = _table [ idx ] ;
while ( _data ) {
// compare hash first
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
break ;
}
_data = _data - > next ;
}
if ( _data ) {
if ( _data - > refcount . ref ( ) ) {
// exists
2022-08-08 17:26:23 +02:00
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2022-03-15 13:29:32 +01:00
lock . unlock ( ) ;
return ;
}
}
_data = memnew ( _Data ) ;
_data - > name = p_name ;
_data - > refcount . init ( ) ;
2022-08-08 17:26:23 +02:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2022-03-15 13:29:32 +01:00
_data - > hash = hash ;
_data - > idx = idx ;
2022-08-08 17:26:23 +02:00
_data - > cname = NULL ;
2022-03-15 13:29:32 +01:00
_data - > next = _table [ idx ] ;
2022-08-08 17:26:23 +02:00
_data - > prev = NULL ;
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2022-03-15 13:29:32 +01:00
if ( _table [ idx ] ) {
_table [ idx ] - > prev = _data ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_table [ idx ] = _data ;
lock . unlock ( ) ;
}
2022-08-08 17:26:23 +02:00
StringName : : StringName ( const StaticCString & p_static_string , bool p_static ) {
_data = NULL ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND ( ! configured ) ;
ERR_FAIL_COND ( ! p_static_string . ptr | | ! p_static_string . ptr [ 0 ] ) ;
lock . lock ( ) ;
uint32_t hash = String : : hash ( p_static_string . ptr ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_data = _table [ idx ] ;
while ( _data ) {
// compare hash first
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_static_string . ptr ) {
break ;
}
_data = _data - > next ;
}
if ( _data ) {
if ( _data - > refcount . ref ( ) ) {
// exists
2022-08-08 17:26:23 +02:00
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2022-03-15 13:29:32 +01:00
lock . unlock ( ) ;
return ;
}
}
_data = memnew ( _Data ) ;
_data - > refcount . init ( ) ;
2022-08-08 17:26:23 +02:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2022-03-15 13:29:32 +01:00
_data - > hash = hash ;
_data - > idx = idx ;
_data - > cname = p_static_string . ptr ;
_data - > next = _table [ idx ] ;
2022-08-08 17:26:23 +02:00
_data - > prev = NULL ;
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2022-03-15 13:29:32 +01:00
if ( _table [ idx ] ) {
_table [ idx ] - > prev = _data ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_table [ idx ] = _data ;
lock . unlock ( ) ;
}
2022-08-08 17:26:23 +02:00
StringName : : StringName ( const String & p_name , bool p_static ) {
2022-03-15 13:29:32 +01:00
_data = nullptr ;
ERR_FAIL_COND ( ! configured ) ;
2022-08-08 17:26:23 +02:00
if ( p_name . empty ( ) ) {
2022-03-15 13:29:32 +01:00
return ;
}
lock . lock ( ) ;
uint32_t hash = p_name . hash ( ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_data = _table [ idx ] ;
while ( _data ) {
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
break ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_data = _data - > next ;
}
if ( _data ) {
if ( _data - > refcount . ref ( ) ) {
// exists
2022-08-08 17:26:23 +02:00
if ( p_static ) {
_data - > static_count . increment ( ) ;
}
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2022-03-15 13:29:32 +01:00
lock . unlock ( ) ;
return ;
}
}
_data = memnew ( _Data ) ;
_data - > name = p_name ;
_data - > refcount . init ( ) ;
2022-08-08 17:26:23 +02:00
_data - > static_count . set ( p_static ? 1 : 0 ) ;
2022-03-15 13:29:32 +01:00
_data - > hash = hash ;
_data - > idx = idx ;
2022-08-08 17:26:23 +02:00
_data - > cname = NULL ;
2022-03-15 13:29:32 +01:00
_data - > next = _table [ idx ] ;
2022-08-08 17:26:23 +02:00
_data - > prev = NULL ;
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
// Keep in memory, force static.
_data - > refcount . ref ( ) ;
_data - > static_count . increment ( ) ;
}
# endif
2022-03-15 13:29:32 +01:00
if ( _table [ idx ] ) {
_table [ idx ] - > prev = _data ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_table [ idx ] = _data ;
lock . unlock ( ) ;
}
StringName StringName : : search ( const char * p_name ) {
ERR_FAIL_COND_V ( ! configured , StringName ( ) ) ;
ERR_FAIL_COND_V ( ! p_name , StringName ( ) ) ;
if ( ! p_name [ 0 ] ) {
return StringName ( ) ;
}
lock . lock ( ) ;
uint32_t hash = String : : hash ( p_name ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_Data * _data = _table [ idx ] ;
while ( _data ) {
// compare hash first
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
break ;
}
_data = _data - > next ;
}
if ( _data & & _data - > refcount . ref ( ) ) {
2022-08-08 17:26:23 +02:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2022-03-15 13:29:32 +01:00
lock . unlock ( ) ;
return StringName ( _data ) ;
}
lock . unlock ( ) ;
return StringName ( ) ; //does not exist
}
StringName StringName : : search ( const CharType * p_name ) {
ERR_FAIL_COND_V ( ! configured , StringName ( ) ) ;
ERR_FAIL_COND_V ( ! p_name , StringName ( ) ) ;
if ( ! p_name [ 0 ] ) {
return StringName ( ) ;
}
lock . lock ( ) ;
uint32_t hash = String : : hash ( p_name ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_Data * _data = _table [ idx ] ;
while ( _data ) {
// compare hash first
if ( _data - > hash = = hash & & _data - > get_name ( ) = = p_name ) {
break ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_data = _data - > next ;
}
if ( _data & & _data - > refcount . ref ( ) ) {
lock . unlock ( ) ;
return StringName ( _data ) ;
}
lock . unlock ( ) ;
return StringName ( ) ; //does not exist
}
StringName StringName : : search ( const String & p_name ) {
ERR_FAIL_COND_V ( p_name = = " " , StringName ( ) ) ;
lock . lock ( ) ;
uint32_t hash = p_name . hash ( ) ;
uint32_t idx = hash & STRING_TABLE_MASK ;
_Data * _data = _table [ idx ] ;
while ( _data ) {
// compare hash first
if ( _data - > hash = = hash & & p_name = = _data - > get_name ( ) ) {
break ;
}
2022-08-08 17:26:23 +02:00
2022-03-15 13:29:32 +01:00
_data = _data - > next ;
}
if ( _data & & _data - > refcount . ref ( ) ) {
2022-08-08 17:26:23 +02:00
# ifdef DEBUG_ENABLED
if ( unlikely ( debug_stringname ) ) {
_data - > debug_references + + ;
}
# endif
2022-03-15 13:29:32 +01:00
lock . unlock ( ) ;
return StringName ( _data ) ;
}
lock . unlock ( ) ;
return StringName ( ) ; //does not exist
}
StringName : : StringName ( ) {
_data = nullptr ;
}
2022-08-08 17:26:23 +02: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 ( ) ;
2022-03-15 13:29:32 +01:00
}
2022-08-08 17:26:23 +02:00
*/