2014-02-10 09:10:30 +08:00
/**************************************************************************/
/* ustring.h */
/**************************************************************************/
/* 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 07:50:27 +08:00
2020-08-05 14:25:28 +08:00
# ifndef USTRING_GODOT_H
# define USTRING_GODOT_H
2014-02-10 09:10:30 +08:00
2022-09-26 19:03:44 +08:00
// Note: _GODOT suffix added to header guard to avoid conflict with ICU header.
2022-02-04 16:32:20 +08:00
# include "core/string/char_utils.h"
2020-11-08 06:33:38 +08:00
# include "core/templates/cowdata.h"
# include "core/templates/vector.h"
2018-09-12 00:13:45 +08:00
# include "core/typedefs.h"
2020-11-08 06:33:38 +08:00
# include "core/variant/array.h"
2014-02-10 09:10:30 +08:00
2024-12-10 20:39:42 +08:00
# include <utility>
2024-12-07 09:28:43 +08:00
/*************************************************************************/
/* Utility Functions */
/*************************************************************************/
// Not defined by std.
// strlen equivalent function for char16_t * arguments.
constexpr size_t strlen ( const char16_t * p_str ) {
const char16_t * ptr = p_str ;
while ( * ptr ! = 0 ) {
+ + ptr ;
}
return ptr - p_str ;
}
// strlen equivalent function for char32_t * arguments.
constexpr size_t strlen ( const char32_t * p_str ) {
const char32_t * ptr = p_str ;
while ( * ptr ! = 0 ) {
+ + ptr ;
}
return ptr - p_str ;
}
// strlen equivalent function for wchar_t * arguments; depends on the platform.
constexpr size_t strlen ( const wchar_t * str ) {
// Use static_cast twice because reinterpret_cast is not allowed in constexpr
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return strlen ( static_cast < const char16_t * > ( static_cast < const void * > ( str ) ) ) ;
# else
// wchar_t is 32-bit
return strlen ( static_cast < const char32_t * > ( static_cast < const void * > ( str ) ) ) ;
# endif
}
constexpr size_t _strlen_clipped ( const char * p_str , int p_clip_to_len ) {
if ( p_clip_to_len < 0 ) {
return strlen ( p_str ) ;
}
int len = 0 ;
while ( len < p_clip_to_len & & * ( p_str + + ) ! = 0 ) {
len + + ;
}
return len ;
}
constexpr size_t _strlen_clipped ( const char32_t * p_str , int p_clip_to_len ) {
if ( p_clip_to_len < 0 ) {
return strlen ( p_str ) ;
}
int len = 0 ;
while ( len < p_clip_to_len & & * ( p_str + + ) ! = 0 ) {
len + + ;
}
return len ;
}
/*************************************************************************/
/* StrRange */
/*************************************************************************/
template < typename Element >
struct StrRange {
const Element * c_str ;
size_t len ;
explicit StrRange ( const std : : nullptr_t p_cstring ) :
c_str ( nullptr ) , len ( 0 ) { }
explicit StrRange ( const Element * p_cstring , const size_t p_len ) :
c_str ( p_cstring ) , len ( p_len ) { }
template < size_t len >
explicit StrRange ( const Element ( & p_cstring ) [ len ] ) :
c_str ( p_cstring ) , len ( strlen ( p_cstring ) ) { }
static StrRange from_c_str ( const Element * p_cstring ) {
return StrRange ( p_cstring , p_cstring ? strlen ( p_cstring ) : 0 ) ;
}
} ;
2020-07-27 18:43:20 +08:00
/*************************************************************************/
/* CharProxy */
/*************************************************************************/
2018-12-16 08:44:18 +08:00
template < typename T >
class CharProxy {
2020-07-27 18:43:20 +08:00
friend class Char16String ;
2018-12-16 08:44:18 +08:00
friend class CharString ;
friend class String ;
const int _index ;
CowData < T > & _cowdata ;
2019-01-05 00:01:54 +08:00
static const T _null = 0 ;
2018-12-16 08:44:18 +08:00
2021-10-01 05:02:41 +08:00
_FORCE_INLINE_ CharProxy ( const int & p_index , CowData < T > & p_cowdata ) :
2018-12-16 08:44:18 +08:00
_index ( p_index ) ,
2021-10-01 05:02:41 +08:00
_cowdata ( p_cowdata ) { }
2018-12-16 08:44:18 +08:00
public :
2021-10-01 05:02:41 +08:00
_FORCE_INLINE_ CharProxy ( const CharProxy < T > & p_other ) :
_index ( p_other . _index ) ,
_cowdata ( p_other . _cowdata ) { }
2018-12-16 08:44:18 +08:00
_FORCE_INLINE_ operator T ( ) const {
2020-05-14 22:41:43 +08:00
if ( unlikely ( _index = = _cowdata . size ( ) ) ) {
2019-01-05 00:01:54 +08:00
return _null ;
2020-05-14 22:41:43 +08:00
}
2019-01-05 00:01:54 +08:00
2018-12-16 08:44:18 +08:00
return _cowdata . get ( _index ) ;
}
_FORCE_INLINE_ const T * operator & ( ) const {
return _cowdata . ptr ( ) + _index ;
}
2021-10-01 05:02:41 +08:00
_FORCE_INLINE_ void operator = ( const T & p_other ) const {
_cowdata . set ( _index , p_other ) ;
2018-12-16 08:44:18 +08:00
}
2021-10-01 05:02:41 +08:00
_FORCE_INLINE_ void operator = ( const CharProxy < T > & p_other ) const {
_cowdata . set ( _index , p_other . operator T ( ) ) ;
2018-12-16 08:44:18 +08:00
}
} ;
2020-07-27 18:43:20 +08:00
/*************************************************************************/
/* Char16String */
/*************************************************************************/
class Char16String {
CowData < char16_t > _cowdata ;
static const char16_t _null ;
public :
_FORCE_INLINE_ char16_t * ptrw ( ) { return _cowdata . ptrw ( ) ; }
_FORCE_INLINE_ const char16_t * ptr ( ) const { return _cowdata . ptr ( ) ; }
_FORCE_INLINE_ int size ( ) const { return _cowdata . size ( ) ; }
Error resize ( int p_size ) { return _cowdata . resize ( p_size ) ; }
_FORCE_INLINE_ char16_t get ( int p_index ) const { return _cowdata . get ( p_index ) ; }
_FORCE_INLINE_ void set ( int p_index , const char16_t & p_elem ) { _cowdata . set ( p_index , p_elem ) ; }
_FORCE_INLINE_ const char16_t & operator [ ] ( int p_index ) const {
if ( unlikely ( p_index = = _cowdata . size ( ) ) ) {
return _null ;
}
return _cowdata . get ( p_index ) ;
}
_FORCE_INLINE_ CharProxy < char16_t > operator [ ] ( int p_index ) { return CharProxy < char16_t > ( p_index , _cowdata ) ; }
_FORCE_INLINE_ Char16String ( ) { }
_FORCE_INLINE_ Char16String ( const Char16String & p_str ) { _cowdata . _ref ( p_str . _cowdata ) ; }
2024-12-10 20:39:42 +08:00
_FORCE_INLINE_ Char16String ( Char16String & & p_str ) :
_cowdata ( std : : move ( p_str . _cowdata ) ) { }
2021-11-30 22:19:26 +08:00
_FORCE_INLINE_ void operator = ( const Char16String & p_str ) { _cowdata . _ref ( p_str . _cowdata ) ; }
2024-12-10 20:39:42 +08:00
_FORCE_INLINE_ void operator = ( Char16String & & p_str ) { _cowdata = std : : move ( p_str . _cowdata ) ; }
2020-07-27 18:43:20 +08:00
_FORCE_INLINE_ Char16String ( const char16_t * p_cstr ) { copy_from ( p_cstr ) ; }
2021-11-30 22:19:26 +08:00
void operator = ( const char16_t * p_cstr ) ;
2020-07-27 18:43:20 +08:00
bool operator < ( const Char16String & p_right ) const ;
Char16String & operator + = ( char16_t p_char ) ;
int length ( ) const { return size ( ) ? size ( ) - 1 : 0 ; }
const char16_t * get_data ( ) const ;
operator const char16_t * ( ) const { return get_data ( ) ; }
2024-12-07 09:28:43 +08:00
explicit operator StrRange < char16_t > ( ) const { return StrRange ( get_data ( ) , length ( ) ) ; }
2020-07-27 18:43:20 +08:00
protected :
void copy_from ( const char16_t * p_cstr ) ;
} ;
/*************************************************************************/
/* CharString */
/*************************************************************************/
2018-07-25 09:11:03 +08:00
class CharString {
CowData < char > _cowdata ;
2019-01-05 00:01:54 +08:00
static const char _null ;
2018-07-25 09:11:03 +08:00
2016-03-09 07:00:52 +08:00
public :
2018-07-25 09:11:03 +08:00
_FORCE_INLINE_ char * ptrw ( ) { return _cowdata . ptrw ( ) ; }
_FORCE_INLINE_ const char * ptr ( ) const { return _cowdata . ptr ( ) ; }
_FORCE_INLINE_ int size ( ) const { return _cowdata . size ( ) ; }
Error resize ( int p_size ) { return _cowdata . resize ( p_size ) ; }
2019-02-22 03:24:29 +08:00
_FORCE_INLINE_ char get ( int p_index ) const { return _cowdata . get ( p_index ) ; }
2018-07-25 09:11:03 +08:00
_FORCE_INLINE_ void set ( int p_index , const char & p_elem ) { _cowdata . set ( p_index , p_elem ) ; }
2019-01-05 00:01:54 +08:00
_FORCE_INLINE_ const char & operator [ ] ( int p_index ) const {
2020-05-14 22:41:43 +08:00
if ( unlikely ( p_index = = _cowdata . size ( ) ) ) {
2019-01-05 00:01:54 +08:00
return _null ;
2020-05-14 22:41:43 +08:00
}
2019-01-05 00:01:54 +08:00
return _cowdata . get ( p_index ) ;
}
2018-12-16 08:44:18 +08:00
_FORCE_INLINE_ CharProxy < char > operator [ ] ( int p_index ) { return CharProxy < char > ( p_index , _cowdata ) ; }
2018-07-25 09:11:03 +08:00
_FORCE_INLINE_ CharString ( ) { }
_FORCE_INLINE_ CharString ( const CharString & p_str ) { _cowdata . _ref ( p_str . _cowdata ) ; }
2024-12-10 20:39:42 +08:00
_FORCE_INLINE_ CharString ( CharString & & p_str ) :
_cowdata ( std : : move ( p_str . _cowdata ) ) { }
2021-11-30 22:19:26 +08:00
_FORCE_INLINE_ void operator = ( const CharString & p_str ) { _cowdata . _ref ( p_str . _cowdata ) ; }
2024-12-10 20:39:42 +08:00
_FORCE_INLINE_ void operator = ( CharString & & p_str ) { _cowdata = std : : move ( p_str . _cowdata ) ; }
2019-03-12 20:57:22 +08:00
_FORCE_INLINE_ CharString ( const char * p_cstr ) { copy_from ( p_cstr ) ; }
2018-07-25 09:11:03 +08:00
2021-11-30 22:19:26 +08:00
void operator = ( const char * p_cstr ) ;
2017-02-21 11:05:15 +08:00
bool operator < ( const CharString & p_right ) const ;
2022-11-18 21:17:37 +08:00
bool operator = = ( const CharString & p_right ) const ;
2018-07-25 09:11:03 +08:00
CharString & operator + = ( char p_char ) ;
2014-02-10 09:10:30 +08:00
int length ( ) const { return size ( ) ? size ( ) - 1 : 0 ; }
const char * get_data ( ) const ;
2018-12-04 04:25:23 +08:00
operator const char * ( ) const { return get_data ( ) ; }
2024-12-07 09:28:43 +08:00
explicit operator StrRange < char > ( ) const { return StrRange ( get_data ( ) , length ( ) ) ; }
2019-03-12 20:57:22 +08:00
protected :
void copy_from ( const char * p_cstr ) ;
2014-02-10 09:10:30 +08:00
} ;
2020-07-27 18:43:20 +08:00
/*************************************************************************/
/* String */
/*************************************************************************/
2016-06-19 06:05:23 +08:00
2018-07-25 09:11:03 +08:00
class String {
2020-07-27 18:43:20 +08:00
CowData < char32_t > _cowdata ;
static const char32_t _null ;
2023-03-11 17:31:41 +08:00
static const char32_t _replacement_char ;
2014-02-10 09:10:30 +08:00
2024-12-07 09:28:43 +08:00
// Known-length copy.
void copy_from ( const StrRange < char > & p_cstr ) ;
void copy_from ( const StrRange < char32_t > & p_cstr ) ;
2020-07-27 18:43:20 +08:00
void copy_from ( const char32_t & p_char ) ;
2024-12-07 09:28:43 +08:00
void copy_from_unchecked ( const char32_t * p_char , int p_length ) ;
// NULL-terminated c string copy - automatically parse the string to find the length.
void copy_from ( const char * p_cstr ) {
copy_from ( StrRange < char > : : from_c_str ( p_cstr ) ) ;
}
void copy_from ( const char * p_cstr , int p_clip_to ) {
copy_from ( StrRange ( p_cstr , p_cstr ? _strlen_clipped ( p_cstr , p_clip_to ) : 0 ) ) ;
}
void copy_from ( const char32_t * p_cstr ) {
copy_from ( StrRange < char32_t > : : from_c_str ( p_cstr ) ) ;
}
void copy_from ( const char32_t * p_cstr , int p_clip_to ) {
copy_from ( StrRange ( p_cstr , p_cstr ? _strlen_clipped ( p_cstr , p_clip_to ) : 0 ) ) ;
}
2020-07-27 18:43:20 +08:00
2024-12-07 09:28:43 +08:00
// wchar_t copy_from depends on the platform.
void copy_from ( const StrRange < wchar_t > & p_cstr ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
parse_utf16 ( ( const char16_t * ) p_cstr . c_str , p_cstr . len ) ;
# else
// wchar_t is 32-bit, copy directly
copy_from ( ( StrRange < char32_t > & ) p_cstr ) ;
# endif
}
void copy_from ( const wchar_t * p_cstr ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
parse_utf16 ( ( const char16_t * ) p_cstr ) ;
# else
// wchar_t is 32-bit, copy directly
copy_from ( ( const char32_t * ) p_cstr ) ;
# endif
}
void copy_from ( const wchar_t * p_cstr , int p_clip_to ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
parse_utf16 ( ( const char16_t * ) p_cstr , p_clip_to ) ;
# else
// wchar_t is 32-bit, copy directly
copy_from ( ( const char32_t * ) p_cstr , p_clip_to ) ;
# endif
}
2020-07-27 18:43:20 +08:00
2016-06-12 11:25:01 +08:00
bool _base_is_subsequence_of ( const String & p_string , bool case_insensitive ) const ;
2019-01-18 16:29:28 +08:00
int _count ( const String & p_string , int p_from , int p_to , bool p_case_insensitive ) const ;
2023-11-03 03:15:48 +08:00
int _count ( const char * p_string , int p_from , int p_to , bool p_case_insensitive ) const ;
2022-08-30 17:36:24 +08:00
String _camelcase_to_underscore ( ) const ;
2014-02-10 09:10:30 +08:00
public :
enum {
npos = - 1 ///<for "some" compatibility with std::string (npos is a huge value in std::string)
} ;
2020-07-27 18:43:20 +08:00
_FORCE_INLINE_ char32_t * ptrw ( ) { return _cowdata . ptrw ( ) ; }
_FORCE_INLINE_ const char32_t * ptr ( ) const { return _cowdata . ptr ( ) ; }
2018-07-25 09:11:03 +08:00
2021-07-04 06:17:03 +08:00
void remove_at ( int p_index ) { _cowdata . remove_at ( p_index ) ; }
2018-07-25 09:11:03 +08:00
_FORCE_INLINE_ void clear ( ) { resize ( 0 ) ; }
2020-07-27 18:43:20 +08:00
_FORCE_INLINE_ char32_t get ( int p_index ) const { return _cowdata . get ( p_index ) ; }
_FORCE_INLINE_ void set ( int p_index , const char32_t & p_elem ) { _cowdata . set ( p_index , p_elem ) ; }
2018-07-25 09:11:03 +08:00
_FORCE_INLINE_ int size ( ) const { return _cowdata . size ( ) ; }
Error resize ( int p_size ) { return _cowdata . resize ( p_size ) ; }
2018-12-16 08:44:18 +08:00
2020-07-27 18:43:20 +08:00
_FORCE_INLINE_ const char32_t & operator [ ] ( int p_index ) const {
2020-05-14 22:41:43 +08:00
if ( unlikely ( p_index = = _cowdata . size ( ) ) ) {
2019-01-05 00:01:54 +08:00
return _null ;
2020-05-14 22:41:43 +08:00
}
2019-01-05 00:01:54 +08:00
return _cowdata . get ( p_index ) ;
}
2020-07-27 18:43:20 +08:00
_FORCE_INLINE_ CharProxy < char32_t > operator [ ] ( int p_index ) { return CharProxy < char32_t > ( p_index , _cowdata ) ; }
2018-07-25 09:11:03 +08:00
2024-12-07 09:28:43 +08:00
/* Compatibility Operators */
2014-02-10 09:10:30 +08:00
bool operator = = ( const String & p_str ) const ;
bool operator ! = ( const String & p_str ) const ;
String operator + ( const String & p_str ) const ;
2022-02-13 20:41:29 +08:00
String operator + ( char32_t p_char ) const ;
2014-02-10 09:10:30 +08:00
String & operator + = ( const String & ) ;
2020-07-27 18:43:20 +08:00
String & operator + = ( char32_t p_char ) ;
2014-02-10 09:10:30 +08:00
String & operator + = ( const char * p_str ) ;
2020-07-27 18:43:20 +08:00
String & operator + = ( const wchar_t * p_str ) ;
String & operator + = ( const char32_t * p_str ) ;
2014-02-10 09:10:30 +08:00
bool operator = = ( const char * p_str ) const ;
2020-07-27 18:43:20 +08:00
bool operator = = ( const wchar_t * p_str ) const ;
bool operator = = ( const char32_t * p_str ) const ;
2024-12-07 09:28:43 +08:00
bool operator = = ( const StrRange < char32_t > & p_str_range ) const ;
2020-07-27 18:43:20 +08:00
2014-02-10 09:10:30 +08:00
bool operator ! = ( const char * p_str ) const ;
2020-07-27 18:43:20 +08:00
bool operator ! = ( const wchar_t * p_str ) const ;
bool operator ! = ( const char32_t * p_str ) const ;
bool operator < ( const char32_t * p_str ) const ;
2014-02-10 09:10:30 +08:00
bool operator < ( const char * p_str ) const ;
2020-07-27 18:43:20 +08:00
bool operator < ( const wchar_t * p_str ) const ;
2017-12-12 06:38:07 +08:00
bool operator < ( const String & p_str ) const ;
bool operator < = ( const String & p_str ) const ;
2020-11-05 10:01:55 +08:00
bool operator > ( const String & p_str ) const ;
bool operator > = ( const String & p_str ) const ;
2014-02-10 09:10:30 +08:00
signed char casecmp_to ( const String & p_str ) const ;
signed char nocasecmp_to ( const String & p_str ) const ;
2023-05-05 02:32:45 +08:00
signed char naturalcasecmp_to ( const String & p_str ) const ;
2017-05-12 03:07:59 +08:00
signed char naturalnocasecmp_to ( const String & p_str ) const ;
2024-02-02 23:50:23 +08:00
// Special sorting for file names. Names starting with `_` are put before all others except those starting with `.`, otherwise natural comparison is used.
signed char filecasecmp_to ( const String & p_str ) const ;
signed char filenocasecmp_to ( const String & p_str ) const ;
2014-02-10 09:10:30 +08:00
2020-07-27 18:43:20 +08:00
const char32_t * get_data ( ) const ;
2014-02-10 09:10:30 +08:00
/* standard size stuff */
2017-12-12 06:38:07 +08:00
_FORCE_INLINE_ int length ( ) const {
int s = size ( ) ;
return s ? ( s - 1 ) : 0 ; // length does not include zero
}
2014-02-10 09:10:30 +08:00
2020-07-27 18:43:20 +08:00
bool is_valid_string ( ) const ;
2022-07-05 20:18:29 +08:00
/* debug, error messages */
void print_unicode_error ( const String & p_message , bool p_critical = false ) const ;
2014-02-10 09:10:30 +08:00
/* complex helpers */
2019-05-03 20:21:04 +08:00
String substr ( int p_from , int p_chars = - 1 ) const ;
2017-12-12 06:38:07 +08:00
int find ( const String & p_str , int p_from = 0 ) const ; ///< return <0 if failed
2019-01-08 01:02:55 +08:00
int find ( const char * p_str , int p_from = 0 ) const ; ///< return <0 if failed
2024-10-11 14:50:50 +08:00
int find_char ( char32_t p_char , int p_from = 0 ) const ; ///< return <0 if failed
2017-12-12 06:38:07 +08:00
int findn ( const String & p_str , int p_from = 0 ) const ; ///< return <0 if failed, case insensitive
2023-11-03 03:15:48 +08:00
int findn ( const char * p_str , int p_from = 0 ) const ; ///< return <0 if failed
2017-12-12 06:38:07 +08:00
int rfind ( const String & p_str , int p_from = - 1 ) const ; ///< return <0 if failed
2023-11-03 03:15:48 +08:00
int rfind ( const char * p_str , int p_from = - 1 ) const ; ///< return <0 if failed
2024-10-11 14:50:50 +08:00
int rfind_char ( char32_t p_char , int p_from = - 1 ) const ; ///< return <0 if failed
2017-12-12 06:38:07 +08:00
int rfindn ( const String & p_str , int p_from = - 1 ) const ; ///< return <0 if failed, case insensitive
2023-11-03 03:15:48 +08:00
int rfindn ( const char * p_str , int p_from = - 1 ) const ; ///< return <0 if failed
2020-04-02 07:20:12 +08:00
int findmk ( const Vector < String > & p_keys , int p_from = 0 , int * r_key = nullptr ) const ; ///< return <0 if failed
2014-02-10 09:10:30 +08:00
bool match ( const String & p_wildcard ) const ;
bool matchn ( const String & p_wildcard ) const ;
bool begins_with ( const String & p_string ) const ;
2016-03-09 07:00:52 +08:00
bool begins_with ( const char * p_string ) const ;
2014-02-10 09:10:30 +08:00
bool ends_with ( const String & p_string ) const ;
2023-11-03 03:15:48 +08:00
bool ends_with ( const char * p_string ) const ;
2017-10-29 05:46:20 +08:00
bool is_enclosed_in ( const String & p_string ) const ;
2016-06-12 11:25:01 +08:00
bool is_subsequence_of ( const String & p_string ) const ;
2022-01-27 07:03:56 +08:00
bool is_subsequence_ofn ( const String & p_string ) const ;
2017-10-29 05:46:20 +08:00
bool is_quoted ( ) const ;
2024-10-11 14:50:50 +08:00
bool is_lowercase ( ) const ;
2016-06-14 01:06:03 +08:00
Vector < String > bigrams ( ) const ;
float similarity ( const String & p_string ) const ;
2023-11-03 01:36:57 +08:00
String format ( const Variant & values , const String & placeholder = " {_} " ) const ;
2017-12-12 06:38:07 +08:00
String replace_first ( const String & p_key , const String & p_with ) const ;
2023-11-03 03:15:48 +08:00
String replace_first ( const char * p_key , const char * p_with ) const ;
2017-12-12 06:38:07 +08:00
String replace ( const String & p_key , const String & p_with ) const ;
String replace ( const char * p_key , const char * p_with ) const ;
String replacen ( const String & p_key , const String & p_with ) const ;
2023-11-03 03:15:48 +08:00
String replacen ( const char * p_key , const char * p_with ) const ;
2019-09-02 11:49:55 +08:00
String repeat ( int p_count ) const ;
2023-06-22 01:40:48 +08:00
String reverse ( ) const ;
2017-12-12 06:38:07 +08:00
String insert ( int p_at_pos , const String & p_string ) const ;
2023-03-30 21:45:45 +08:00
String erase ( int p_pos , int p_chars = 1 ) const ;
2014-02-10 09:10:30 +08:00
String pad_decimals ( int p_digits ) const ;
String pad_zeros ( int p_digits ) const ;
2018-04-13 22:40:27 +08:00
String trim_prefix ( const String & p_prefix ) const ;
2023-11-03 03:15:48 +08:00
String trim_prefix ( const char * p_prefix ) const ;
2018-04-13 22:40:27 +08:00
String trim_suffix ( const String & p_suffix ) const ;
2023-11-03 03:15:48 +08:00
String trim_suffix ( const char * p_suffix ) const ;
2015-02-02 02:42:36 +08:00
String lpad ( int min_length , const String & character = " " ) const ;
String rpad ( int min_length , const String & character = " " ) const ;
2015-02-19 23:45:49 +08:00
String sprintf ( const Array & values , bool * error ) const ;
2023-11-03 01:36:57 +08:00
String quote ( const String & quotechar = " \" " ) const ;
2017-10-29 05:46:20 +08:00
String unquote ( ) const ;
2014-02-10 09:10:30 +08:00
static String num ( double p_num , int p_decimals = - 1 ) ;
static String num_scientific ( double p_num ) ;
2021-02-25 22:54:50 +08:00
static String num_real ( double p_num , bool p_trailing = true ) ;
2024-03-27 00:04:14 +08:00
static String num_real ( float p_num , bool p_trailing = true ) ;
2015-02-02 02:42:36 +08:00
static String num_int64 ( int64_t p_num , int base = 10 , bool capitalize_hex = false ) ;
2018-02-22 20:13:51 +08:00
static String num_uint64 ( uint64_t p_num , int base = 10 , bool capitalize_hex = false ) ;
2020-07-27 18:43:20 +08:00
static String chr ( char32_t p_char ) ;
2014-02-10 09:10:30 +08:00
static String md5 ( const uint8_t * p_md5 ) ;
2016-06-17 15:55:16 +08:00
static String hex_encode_buffer ( const uint8_t * p_buffer , int p_len ) ;
2023-03-06 11:17:33 +08:00
Vector < uint8_t > hex_decode ( ) const ;
2014-02-10 09:10:30 +08:00
bool is_numeric ( ) const ;
2020-07-27 18:43:20 +08:00
double to_float ( ) const ;
2021-01-28 20:39:05 +08:00
int64_t hex_to_int ( ) const ;
int64_t bin_to_int ( ) const ;
2020-05-13 17:31:51 +08:00
int64_t to_int ( ) const ;
2020-07-27 18:43:20 +08:00
2020-06-03 12:04:04 +08:00
static int64_t to_int ( const char * p_str , int p_len = - 1 ) ;
2020-07-27 18:43:20 +08:00
static int64_t to_int ( const wchar_t * p_str , int p_len = - 1 ) ;
static int64_t to_int ( const char32_t * p_str , int p_len = - 1 , bool p_clamp = false ) ;
2020-07-25 02:07:57 +08:00
static double to_float ( const char * p_str ) ;
2020-07-27 18:43:20 +08:00
static double to_float ( const wchar_t * p_str , const wchar_t * * r_end = nullptr ) ;
static double to_float ( const char32_t * p_str , const char32_t * * r_end = nullptr ) ;
2023-04-06 23:54:56 +08:00
static uint32_t num_characters ( int64_t p_int ) ;
2020-07-27 18:43:20 +08:00
2014-02-10 09:10:30 +08:00
String capitalize ( ) const ;
2022-08-30 17:36:24 +08:00
String to_camel_case ( ) const ;
String to_pascal_case ( ) const ;
String to_snake_case ( ) const ;
2014-02-10 09:10:30 +08:00
2019-06-16 10:45:24 +08:00
String get_with_code_lines ( ) const ;
2023-11-03 01:36:57 +08:00
int get_slice_count ( const String & p_splitter ) const ;
2023-11-03 03:15:48 +08:00
int get_slice_count ( const char * p_splitter ) const ;
2023-11-03 01:36:57 +08:00
String get_slice ( const String & p_splitter , int p_slice ) const ;
2023-11-03 03:15:48 +08:00
String get_slice ( const char * p_splitter , int p_slice ) const ;
2020-07-27 18:43:20 +08:00
String get_slicec ( char32_t p_splitter , int p_slice ) const ;
2014-02-10 09:10:30 +08:00
2022-08-12 22:21:15 +08:00
Vector < String > split ( const String & p_splitter = " " , bool p_allow_empty = true , int p_maxsplit = 0 ) const ;
2023-11-03 03:15:48 +08:00
Vector < String > split ( const char * p_splitter = " " , bool p_allow_empty = true , int p_maxsplit = 0 ) const ;
2022-08-12 22:21:15 +08:00
Vector < String > rsplit ( const String & p_splitter = " " , bool p_allow_empty = true , int p_maxsplit = 0 ) const ;
2023-11-03 03:15:48 +08:00
Vector < String > rsplit ( const char * p_splitter = " " , bool p_allow_empty = true , int p_maxsplit = 0 ) const ;
2014-10-10 06:44:27 +08:00
Vector < String > split_spaces ( ) const ;
2022-11-20 19:29:50 +08:00
Vector < double > split_floats ( const String & p_splitter , bool p_allow_empty = true ) const ;
2014-02-10 09:10:30 +08:00
Vector < float > split_floats_mk ( const Vector < String > & p_splitters , bool p_allow_empty = true ) const ;
Vector < int > split_ints ( const String & p_splitter , bool p_allow_empty = true ) const ;
Vector < int > split_ints_mk ( const Vector < String > & p_splitters , bool p_allow_empty = true ) const ;
2024-01-09 09:36:19 +08:00
String join ( const Vector < String > & parts ) const ;
2017-10-02 23:01:43 +08:00
2020-07-27 18:43:20 +08:00
static char32_t char_uppercase ( char32_t p_char ) ;
static char32_t char_lowercase ( char32_t p_char ) ;
2014-02-10 09:10:30 +08:00
String to_upper ( ) const ;
String to_lower ( ) const ;
2019-01-18 16:29:28 +08:00
int count ( const String & p_string , int p_from = 0 , int p_to = 0 ) const ;
2023-11-03 03:15:48 +08:00
int count ( const char * p_string , int p_from = 0 , int p_to = 0 ) const ;
2019-01-18 16:29:28 +08:00
int countn ( const String & p_string , int p_from = 0 , int p_to = 0 ) const ;
2023-11-03 03:15:48 +08:00
int countn ( const char * p_string , int p_from = 0 , int p_to = 0 ) const ;
2019-01-18 16:29:28 +08:00
2022-06-12 10:45:58 +08:00
String left ( int p_len ) const ;
String right ( int p_len ) const ;
2021-12-15 23:01:06 +08:00
String indent ( const String & p_prefix ) const ;
2017-10-11 16:27:54 +08:00
String dedent ( ) const ;
2016-05-11 15:22:59 +08:00
String strip_edges ( bool left = true , bool right = true ) const ;
2014-02-10 09:10:30 +08:00
String strip_escapes ( ) const ;
2018-07-25 09:11:03 +08:00
String lstrip ( const String & p_chars ) const ;
String rstrip ( const String & p_chars ) const ;
2017-01-14 11:51:09 +08:00
String get_extension ( ) const ;
String get_basename ( ) const ;
2022-08-30 08:34:01 +08:00
String path_join ( const String & p_file ) const ;
2020-11-23 19:47:11 +08:00
char32_t unicode_at ( int p_idx ) const ;
2014-02-10 09:10:30 +08:00
CharString ascii ( bool p_allow_extended = false ) const ;
CharString utf8 ( ) const ;
2022-08-01 00:46:53 +08:00
Error parse_utf8 ( const char * p_utf8 , int p_len = - 1 , bool p_skip_cr = false ) ;
2014-02-10 09:10:30 +08:00
static String utf8 ( const char * p_utf8 , int p_len = - 1 ) ;
2016-03-09 07:00:52 +08:00
2020-07-27 18:43:20 +08:00
Char16String utf16 ( ) const ;
2024-03-24 01:18:50 +08:00
Error parse_utf16 ( const char16_t * p_utf16 , int p_len = - 1 , bool p_default_little_endian = true ) ;
2020-07-27 18:43:20 +08:00
static String utf16 ( const char16_t * p_utf16 , int p_len = - 1 ) ;
static uint32_t hash ( const char32_t * p_cstr , int p_len ) ; /* hash the string */
static uint32_t hash ( const char32_t * p_cstr ) ; /* hash the string */
static uint32_t hash ( const wchar_t * p_cstr , int p_len ) ; /* hash the string */
static uint32_t hash ( const wchar_t * p_cstr ) ; /* hash the string */
2014-02-10 09:10:30 +08:00
static uint32_t hash ( const char * p_cstr , int p_len ) ; /* hash the string */
static uint32_t hash ( const char * p_cstr ) ; /* hash the string */
uint32_t hash ( ) const ; /* hash the string */
2016-03-09 07:00:52 +08:00
uint64_t hash64 ( ) const ; /* hash the string */
2014-03-14 09:57:24 +08:00
String md5_text ( ) const ;
2019-07-02 22:07:02 +08:00
String sha1_text ( ) const ;
2016-06-17 15:55:16 +08:00
String sha256_text ( ) const ;
2014-08-02 09:10:38 +08:00
Vector < uint8_t > md5_buffer ( ) const ;
2019-07-02 22:07:02 +08:00
Vector < uint8_t > sha1_buffer ( ) const ;
2016-06-24 00:57:45 +08:00
Vector < uint8_t > sha256_buffer ( ) const ;
2014-08-02 09:10:38 +08:00
2020-12-15 20:04:21 +08:00
_FORCE_INLINE_ bool is_empty ( ) const { return length ( ) = = 0 ; }
2022-02-04 00:03:38 +08:00
_FORCE_INLINE_ bool contains ( const char * p_str ) const { return find ( p_str ) ! = - 1 ; }
_FORCE_INLINE_ bool contains ( const String & p_str ) const { return find ( p_str ) ! = - 1 ; }
2024-12-06 00:56:08 +08:00
_FORCE_INLINE_ bool contains_char ( char32_t p_chr ) const { return find_char ( p_chr ) ! = - 1 ; }
2024-05-06 16:26:10 +08:00
_FORCE_INLINE_ bool containsn ( const char * p_str ) const { return findn ( p_str ) ! = - 1 ; }
_FORCE_INLINE_ bool containsn ( const String & p_str ) const { return findn ( p_str ) ! = - 1 ; }
2014-02-10 09:10:30 +08:00
// path functions
2021-06-03 21:41:22 +08:00
bool is_absolute_path ( ) const ;
2021-08-30 07:43:47 +08:00
bool is_relative_path ( ) const ;
2014-02-10 09:10:30 +08:00
bool is_resource_file ( ) const ;
String path_to ( const String & p_path ) const ;
String path_to_file ( const String & p_path ) const ;
String get_base_dir ( ) const ;
String get_file ( ) const ;
2019-10-04 19:35:01 +08:00
static String humanize_size ( uint64_t p_size ) ;
2014-02-10 09:10:30 +08:00
String simplify_path ( ) const ;
2022-01-24 19:12:46 +08:00
bool is_network_share_path ( ) const ;
2014-02-10 09:10:30 +08:00
String xml_escape ( bool p_escape_quotes = false ) const ;
String xml_unescape ( ) const ;
2020-11-30 11:43:38 +08:00
String uri_encode ( ) const ;
String uri_decode ( ) const ;
2014-02-10 09:10:30 +08:00
String c_escape ( ) const ;
2017-01-17 01:03:38 +08:00
String c_escape_multiline ( ) const ;
2014-02-10 09:10:30 +08:00
String c_unescape ( ) const ;
2016-01-11 02:01:06 +08:00
String json_escape ( ) const ;
2024-05-22 10:22:50 +08:00
Error parse_url ( String & r_scheme , String & r_host , int & r_port , String & r_path , String & r_fragment ) const ;
2016-03-09 07:00:52 +08:00
2019-12-20 10:03:15 +08:00
String property_name_encode ( ) const ;
2021-01-29 04:48:12 +08:00
// node functions
2023-11-09 22:37:08 +08:00
static String get_invalid_node_name_characters ( bool p_allow_internal = false ) ;
2021-01-29 04:48:12 +08:00
String validate_node_name ( ) const ;
2024-08-23 14:30:51 +08:00
String validate_ascii_identifier ( ) const ;
2024-09-22 19:31:58 +08:00
String validate_unicode_identifier ( ) const ;
2022-07-19 08:58:27 +08:00
String validate_filename ( ) const ;
2021-01-29 04:48:12 +08:00
2024-08-23 14:30:51 +08:00
bool is_valid_ascii_identifier ( ) const ;
bool is_valid_unicode_identifier ( ) const ;
2021-06-17 00:24:34 +08:00
bool is_valid_int ( ) const ;
2014-02-10 09:10:30 +08:00
bool is_valid_float ( ) const ;
2016-10-20 20:58:00 +08:00
bool is_valid_hex_number ( bool p_with_prefix ) const ;
2014-02-10 09:10:30 +08:00
bool is_valid_html_color ( ) const ;
bool is_valid_ip_address ( ) const ;
2019-04-09 06:18:03 +08:00
bool is_valid_filename ( ) const ;
2014-02-10 09:10:30 +08:00
2024-08-23 14:30:51 +08:00
// Use `is_valid_ascii_identifier()` instead. Kept for compatibility.
bool is_valid_identifier ( ) const { return is_valid_ascii_identifier ( ) ; }
2014-02-10 09:10:30 +08:00
/**
* The constructors must not depend on other overloads
*/
2018-07-25 09:11:03 +08:00
_FORCE_INLINE_ String ( ) { }
_FORCE_INLINE_ String ( const String & p_str ) { _cowdata . _ref ( p_str . _cowdata ) ; }
2024-12-10 20:39:42 +08:00
_FORCE_INLINE_ String ( String & & p_str ) :
_cowdata ( std : : move ( p_str . _cowdata ) ) { }
2021-11-30 22:19:26 +08:00
_FORCE_INLINE_ void operator = ( const String & p_str ) { _cowdata . _ref ( p_str . _cowdata ) ; }
2024-12-10 20:39:42 +08:00
_FORCE_INLINE_ void operator = ( String & & p_str ) { _cowdata = std : : move ( p_str . _cowdata ) ; }
2018-07-25 09:11:03 +08:00
2020-10-14 02:59:37 +08:00
Vector < uint8_t > to_ascii_buffer ( ) const ;
Vector < uint8_t > to_utf8_buffer ( ) const ;
Vector < uint8_t > to_utf16_buffer ( ) const ;
Vector < uint8_t > to_utf32_buffer ( ) const ;
2023-02-13 22:18:12 +08:00
Vector < uint8_t > to_wchar_buffer ( ) const ;
2020-10-14 02:59:37 +08:00
2024-12-07 09:28:43 +08:00
// Constructors for NULL terminated C strings.
String ( const char * p_cstr ) {
copy_from ( p_cstr ) ;
}
String ( const wchar_t * p_cstr ) {
copy_from ( p_cstr ) ;
}
String ( const char32_t * p_cstr ) {
copy_from ( p_cstr ) ;
}
String ( const char * p_cstr , int p_clip_to_len ) {
copy_from ( p_cstr , p_clip_to_len ) ;
}
String ( const wchar_t * p_cstr , int p_clip_to_len ) {
copy_from ( p_cstr , p_clip_to_len ) ;
}
String ( const char32_t * p_cstr , int p_clip_to_len ) {
copy_from ( p_cstr , p_clip_to_len ) ;
}
// Copy assignment for NULL terminated C strings.
void operator = ( const char * p_cstr ) {
copy_from ( p_cstr ) ;
}
void operator = ( const wchar_t * p_cstr ) {
copy_from ( p_cstr ) ;
}
void operator = ( const char32_t * p_cstr ) {
copy_from ( p_cstr ) ;
}
explicit operator StrRange < char32_t > ( ) const { return StrRange ( get_data ( ) , length ( ) ) ; }
2014-02-10 09:10:30 +08:00
} ;
bool operator = = ( const char * p_chr , const String & p_str ) ;
2020-07-27 18:43:20 +08:00
bool operator = = ( const wchar_t * p_chr , const String & p_str ) ;
2020-11-05 10:01:55 +08:00
bool operator ! = ( const char * p_chr , const String & p_str ) ;
bool operator ! = ( const wchar_t * p_chr , const String & p_str ) ;
2014-02-10 09:10:30 +08:00
String operator + ( const char * p_chr , const String & p_str ) ;
2020-07-27 18:43:20 +08:00
String operator + ( const wchar_t * p_chr , const String & p_str ) ;
String operator + ( char32_t p_chr , const String & p_str ) ;
2014-02-10 09:10:30 +08:00
String itos ( int64_t p_val ) ;
2019-11-01 23:16:31 +08:00
String uitos ( uint64_t p_val ) ;
2014-02-10 09:10:30 +08:00
String rtos ( double p_val ) ;
String rtoss ( double p_val ) ; //scientific version
struct NoCaseComparator {
bool operator ( ) ( const String & p_a , const String & p_b ) const {
return p_a . nocasecmp_to ( p_b ) < 0 ;
}
} ;
2017-05-12 03:07:59 +08:00
struct NaturalNoCaseComparator {
bool operator ( ) ( const String & p_a , const String & p_b ) const {
return p_a . naturalnocasecmp_to ( p_b ) < 0 ;
}
} ;
2024-02-02 23:50:23 +08:00
struct FileNoCaseComparator {
bool operator ( ) ( const String & p_a , const String & p_b ) const {
return p_a . filenocasecmp_to ( p_b ) < 0 ;
}
} ;
2017-12-16 22:31:30 +08:00
template < typename L , typename R >
_FORCE_INLINE_ bool is_str_less ( const L * l_ptr , const R * r_ptr ) {
while ( true ) {
2020-07-27 18:43:20 +08:00
const char32_t l = * l_ptr ;
const char32_t r = * r_ptr ;
if ( l = = 0 & & r = = 0 ) {
2017-12-16 22:31:30 +08:00
return false ;
2020-07-27 18:43:20 +08:00
} else if ( l = = 0 ) {
2017-12-16 22:31:30 +08:00
return true ;
2020-07-27 18:43:20 +08:00
} else if ( r = = 0 ) {
2017-12-16 22:31:30 +08:00
return false ;
2020-07-27 18:43:20 +08:00
} else if ( l < r ) {
2017-12-16 22:31:30 +08:00
return true ;
2020-07-27 18:43:20 +08:00
} else if ( l > r ) {
2017-12-16 22:31:30 +08:00
return false ;
2020-05-14 22:41:43 +08:00
}
2017-12-16 22:31:30 +08:00
l_ptr + + ;
r_ptr + + ;
}
}
2014-02-10 09:10:30 +08:00
/* end of namespace */
2020-03-19 01:34:36 +08:00
// Tool translate (TTR and variants) for the editor UI,
// and doc translate for the class reference (DTR).
2016-05-18 05:27:15 +08:00
# ifdef TOOLS_ENABLED
2020-03-19 01:34:36 +08:00
// Gets parsed.
2020-07-16 16:52:06 +08:00
String TTR ( const String & p_text , const String & p_context = " " ) ;
String TTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context = " " ) ;
String DTR ( const String & p_text , const String & p_context = " " ) ;
String DTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context = " " ) ;
2020-03-19 01:34:36 +08:00
// Use for C strings.
2019-06-17 03:57:34 +08:00
# define TTRC(m_value) (m_value)
2020-03-19 01:34:36 +08:00
// Use to avoid parsing (for use later with C strings).
2019-04-09 06:18:03 +08:00
# define TTRGET(m_value) TTR(m_value)
2014-02-10 09:10:30 +08:00
2016-05-18 05:27:15 +08:00
# else
2019-04-09 06:18:03 +08:00
# define TTRC(m_value) (m_value)
2019-06-17 03:57:34 +08:00
# define TTRGET(m_value) (m_value)
2016-05-18 05:27:15 +08:00
# endif
2022-05-19 14:08:47 +08:00
// Use this to mark property names for editor translation.
// Often for dynamic properties defined in _get_property_list().
// Property names defined directly inside EDITOR_DEF, GLOBAL_DEF, and ADD_PROPERTY macros don't need this.
# define PNAME(m_value) (m_value)
// Similar to PNAME, but to mark groups, i.e. properties with PROPERTY_USAGE_GROUP.
// Groups defined directly inside ADD_GROUP macros don't need this.
// The arguments are the same as ADD_GROUP. m_prefix is only used for extraction.
# define GNAME(m_value, m_prefix) (m_value)
2020-03-19 01:34:36 +08:00
// Runtime translate for the public node API.
2020-07-16 16:52:06 +08:00
String RTR ( const String & p_text , const String & p_context = " " ) ;
String RTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context = " " ) ;
2016-05-18 05:27:15 +08:00
2023-12-16 07:56:06 +08:00
/**
* " Extractable TRanslate " . Used for strings that can appear inside an exported
* project ( such as the ones in nodes like ` FileDialog ` ) , which are made possible
* to add in the POT generator . A translation context can optionally be specified
* to disambiguate between identical source strings in translations .
* When placeholders are desired , use vformat ( ETR ( " Example: %s " ) , some_string ) ` .
* If a string mentions a quantity ( and may therefore need a dynamic plural form ) ,
* use ` ETRN ( ) ` instead of ` ETR ( ) ` .
*
* NOTE : This function is for string extraction only , and will just return the
* string it was given . The translation itself should be done internally by nodes
* with ` atr ( ) ` instead .
*/
_FORCE_INLINE_ String ETR ( const String & p_text , const String & p_context = " " ) {
return p_text ;
}
/**
* " Extractable TRanslate for N items " . Used for strings that can appear inside an
* exported project ( such as the ones in nodes like ` FileDialog ` ) , which are made
* possible to add in the POT generator . A translation context can optionally be
* specified to disambiguate between identical source strings in translations .
* Use ` ETR ( ) ` if the string doesn ' t need dynamic plural form . When placeholders
* are desired , use ` vformat ( ETRN ( " %d item " , " %d items " , some_integer ) , some_integer ) ` .
* The placeholder must be present in both strings to avoid run - time warnings in ` vformat ( ) ` .
*
* NOTE : This function is for string extraction only , and will just return the
* string it was given . The translation itself should be done internally by nodes
* with ` atr ( ) ` instead .
*/
_FORCE_INLINE_ String ETRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context = " " ) {
if ( p_n = = 1 ) {
return p_text ;
}
return p_text_plural ;
}
2017-12-23 16:59:54 +08:00
bool select_word ( const String & p_s , int p_col , int & r_beg , int & r_end ) ;
2020-11-11 05:31:33 +08:00
_FORCE_INLINE_ void sarray_add_str ( Vector < String > & arr ) {
}
_FORCE_INLINE_ void sarray_add_str ( Vector < String > & arr , const String & p_str ) {
arr . push_back ( p_str ) ;
}
template < typename . . . P >
_FORCE_INLINE_ void sarray_add_str ( Vector < String > & arr , const String & p_str , P . . . p_args ) {
arr . push_back ( p_str ) ;
sarray_add_str ( arr , p_args . . . ) ;
}
template < typename . . . P >
_FORCE_INLINE_ Vector < String > sarray ( P . . . p_args ) {
Vector < String > arr ;
sarray_add_str ( arr , p_args . . . ) ;
return arr ;
}
2020-08-05 14:25:28 +08:00
# endif // USTRING_GODOT_H