2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* ustring.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
2014-02-10 02:10:30 +01:00
# include "ustring.h"
2017-01-16 08:04:19 +01:00
2019-07-11 15:21:47 +02:00
# include "core/crypto/crypto_core.h"
2020-11-07 23:33:38 +01:00
# include "core/math/color.h"
2018-09-11 18:13:45 +02:00
# include "core/math/math_funcs.h"
# include "core/os/memory.h"
2020-11-07 23:33:38 +01:00
# include "core/string/print_string.h"
2022-04-16 12:23:32 +02:00
# include "core/string/string_name.h"
2020-11-07 23:33:38 +01:00
# include "core/string/translation.h"
# include "core/string/ucaps.h"
# include "core/variant/variant.h"
2021-11-15 10:39:00 +01:00
# include "core/version_generated.gen.h"
2014-02-10 02:10:30 +01:00
# include <stdio.h>
# include <stdlib.h>
2021-08-18 14:35:24 +02:00
# include <cstdint>
2015-11-19 22:09:57 +01:00
2020-05-12 17:01:17 +02:00
# ifdef _MSC_VER
# define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
# endif
2015-11-19 22:09:57 +01:00
# if defined(MINGW_ENABLED) || defined(_MSC_VER)
2018-10-04 15:38:52 +02:00
# define snprintf _snprintf_s
2015-11-19 22:09:57 +01:00
# endif
2021-08-12 19:48:42 +02:00
static const int MAX_DECIMALS = 32 ;
static _FORCE_INLINE_ char32_t lower_case ( char32_t c ) {
2022-02-04 09:32:20 +01:00
return ( is_ascii_upper_case ( c ) ? ( c + ( ' a ' - ' A ' ) ) : c ) ;
2021-08-12 19:48:42 +02:00
}
2017-02-21 04:05:15 +01:00
2019-01-04 17:01:54 +01:00
const char CharString : : _null = 0 ;
2020-07-27 12:43:20 +02:00
const char16_t Char16String : : _null = 0 ;
const char32_t String : : _null = 0 ;
2023-03-11 10:31:41 +01:00
const char32_t String : : _replacement_char = 0xfffd ;
2019-01-04 17:01:54 +01:00
2017-12-23 09:59:54 +01:00
bool select_word ( const String & p_s , int p_col , int & r_beg , int & r_end ) {
const String & s = p_s ;
int beg = CLAMP ( p_col , 0 , s . length ( ) ) ;
int end = beg ;
if ( s [ beg ] > 32 | | beg = = s . length ( ) ) {
bool symbol = beg < s . length ( ) & & is_symbol ( s [ beg ] ) ;
while ( beg > 0 & & s [ beg - 1 ] > 32 & & ( symbol = = is_symbol ( s [ beg - 1 ] ) ) ) {
beg - - ;
}
while ( end < s . length ( ) & & s [ end + 1 ] > 32 & & ( symbol = = is_symbol ( s [ end + 1 ] ) ) ) {
end + + ;
}
2020-05-14 16:41:43 +02:00
if ( end < s . length ( ) ) {
2017-12-23 09:59:54 +01:00
end + = 1 ;
2020-05-14 16:41:43 +02:00
}
2017-12-23 09:59:54 +01:00
r_beg = beg ;
r_end = end ;
return true ;
} else {
return false ;
}
}
2020-07-27 12:43:20 +02:00
/*************************************************************************/
/* Char16String */
/*************************************************************************/
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
bool Char16String : : operator < ( const Char16String & p_right ) const {
2017-02-21 04:05:15 +01:00
if ( length ( ) = = 0 ) {
return p_right . length ( ) ! = 0 ;
}
2017-12-16 15:31:30 +01:00
return is_str_less ( get_data ( ) , p_right . get_data ( ) ) ;
2017-02-21 04:05:15 +01:00
}
2020-07-27 12:43:20 +02:00
Char16String & Char16String : : operator + = ( char16_t p_char ) {
2022-02-21 22:14:28 +01:00
const int lhs_len = length ( ) ;
resize ( lhs_len + 2 ) ;
char16_t * dst = ptrw ( ) ;
dst [ lhs_len ] = p_char ;
dst [ lhs_len + 1 ] = 0 ;
2018-07-25 03:11:03 +02:00
return * this ;
}
2021-11-30 15:19:26 +01:00
void Char16String : : operator = ( const char16_t * p_cstr ) {
2020-07-27 12:43:20 +02:00
copy_from ( p_cstr ) ;
}
const char16_t * Char16String : : get_data ( ) const {
2020-05-14 16:41:43 +02:00
if ( size ( ) ) {
2014-02-10 02:10:30 +01:00
return & operator [ ] ( 0 ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-07-27 12:43:20 +02:00
return u " " ;
}
}
void Char16String : : copy_from ( const char16_t * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
const char16_t * s = p_cstr ;
for ( ; * s ; s + + ) {
}
size_t len = s - p_cstr ;
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
Error err = resize ( + + len ) ; // include terminating null char
ERR_FAIL_COND_MSG ( err ! = OK , " Failed to copy char16_t string. " ) ;
memcpy ( ptrw ( ) , p_cstr , len * sizeof ( char16_t ) ) ;
}
/*************************************************************************/
/* CharString */
/*************************************************************************/
bool CharString : : operator < ( const CharString & p_right ) const {
if ( length ( ) = = 0 ) {
return p_right . length ( ) ! = 0 ;
}
return is_str_less ( get_data ( ) , p_right . get_data ( ) ) ;
}
2022-11-18 14:17:37 +01:00
bool CharString : : operator = = ( const CharString & p_right ) const {
if ( length ( ) = = 0 ) {
// True if both have length 0, false if only p_right has a length
return p_right . length ( ) = = 0 ;
} else if ( p_right . length ( ) = = 0 ) {
// False due to unequal length
return false ;
}
return strcmp ( ptr ( ) , p_right . ptr ( ) ) = = 0 ;
}
2020-07-27 12:43:20 +02:00
CharString & CharString : : operator + = ( char p_char ) {
2022-02-21 22:14:28 +01:00
const int lhs_len = length ( ) ;
resize ( lhs_len + 2 ) ;
char * dst = ptrw ( ) ;
dst [ lhs_len ] = p_char ;
dst [ lhs_len + 1 ] = 0 ;
2020-07-27 12:43:20 +02:00
return * this ;
2014-02-10 02:10:30 +01:00
}
2021-11-30 15:19:26 +01:00
void CharString : : operator = ( const char * p_cstr ) {
2019-03-12 13:57:22 +01:00
copy_from ( p_cstr ) ;
}
2020-07-27 12:43:20 +02:00
const char * CharString : : get_data ( ) const {
if ( size ( ) ) {
return & operator [ ] ( 0 ) ;
} else {
return " " ;
}
}
2019-03-12 13:57:22 +01:00
void CharString : : copy_from ( const char * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
size_t len = strlen ( p_cstr ) ;
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
2019-11-18 21:51:54 +01:00
Error err = resize ( + + len ) ; // include terminating null char
2019-03-12 13:57:22 +01:00
2019-11-18 21:51:54 +01:00
ERR_FAIL_COND_MSG ( err ! = OK , " Failed to copy C-string. " ) ;
memcpy ( ptrw ( ) , p_cstr , len ) ;
2019-03-12 13:57:22 +01:00
}
2020-07-27 12:43:20 +02:00
/*************************************************************************/
/* String */
/*************************************************************************/
2021-04-26 09:33:28 +02:00
Error String : : parse_url ( String & r_scheme , String & r_host , int & r_port , String & r_path ) const {
// Splits the URL into scheme, host, port, path. Strip credentials when present.
String base = * this ;
r_scheme = " " ;
r_host = " " ;
r_port = 0 ;
r_path = " " ;
int pos = base . find ( " :// " ) ;
// Scheme
if ( pos ! = - 1 ) {
r_scheme = base . substr ( 0 , pos + 3 ) . to_lower ( ) ;
base = base . substr ( pos + 3 , base . length ( ) - pos - 3 ) ;
}
pos = base . find ( " / " ) ;
// Path
if ( pos ! = - 1 ) {
r_path = base . substr ( pos , base . length ( ) - pos ) ;
base = base . substr ( 0 , pos ) ;
}
// Host
pos = base . find ( " @ " ) ;
if ( pos ! = - 1 ) {
// Strip credentials
base = base . substr ( pos + 1 , base . length ( ) - pos - 1 ) ;
}
if ( base . begins_with ( " [ " ) ) {
// Literal IPv6
pos = base . rfind ( " ] " ) ;
if ( pos = = - 1 ) {
return ERR_INVALID_PARAMETER ;
}
r_host = base . substr ( 1 , pos - 1 ) ;
base = base . substr ( pos + 1 , base . length ( ) - pos - 1 ) ;
} else {
// Anything else
2021-06-11 10:09:05 +02:00
if ( base . get_slice_count ( " : " ) > 2 ) {
2021-04-26 09:33:28 +02:00
return ERR_INVALID_PARAMETER ;
}
pos = base . rfind ( " : " ) ;
if ( pos = = - 1 ) {
r_host = base ;
base = " " ;
} else {
r_host = base . substr ( 0 , pos ) ;
base = base . substr ( pos , base . length ( ) - pos ) ;
}
}
if ( r_host . is_empty ( ) ) {
return ERR_INVALID_PARAMETER ;
}
r_host = r_host . to_lower ( ) ;
// Port
if ( base . begins_with ( " : " ) ) {
base = base . substr ( 1 , base . length ( ) - 1 ) ;
2021-06-16 18:24:34 +02:00
if ( ! base . is_valid_int ( ) ) {
2021-04-26 09:33:28 +02:00
return ERR_INVALID_PARAMETER ;
}
r_port = base . to_int ( ) ;
if ( r_port < 1 | | r_port > 65535 ) {
return ERR_INVALID_PARAMETER ;
}
}
return OK ;
}
2014-02-10 02:10:30 +01:00
void String : : copy_from ( const char * p_cstr ) {
2020-07-27 12:43:20 +02:00
// copy Latin-1 encoded c-string directly
2017-06-25 15:57:06 +02:00
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
2022-02-21 22:14:28 +01:00
const size_t len = strlen ( p_cstr ) ;
2014-02-10 02:10:30 +01:00
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
resize ( len + 1 ) ; // include 0
2016-03-09 00:00:52 +01:00
2024-01-28 21:51:39 +01:00
char32_t * dst = ptrw ( ) ;
2015-06-30 16:59:00 +02:00
2022-02-21 22:14:28 +01:00
for ( size_t i = 0 ; i < = len ; i + + ) {
2023-11-29 10:31:52 +01:00
# if CHAR_MIN == 0
uint8_t c = p_cstr [ i ] ;
# else
2022-07-05 14:18:29 +02:00
uint8_t c = p_cstr [ i ] > = 0 ? p_cstr [ i ] : uint8_t ( 256 + p_cstr [ i ] ) ;
2023-11-29 10:31:52 +01:00
# endif
2022-07-05 14:18:29 +02:00
if ( c = = 0 & & i < len ) {
print_unicode_error ( " NUL character " , true ) ;
2023-03-11 10:31:41 +01:00
dst [ i ] = _replacement_char ;
2022-07-05 14:18:29 +02:00
} else {
dst [ i ] = c ;
}
2014-02-10 02:10:30 +01:00
}
}
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char * p_cstr , const int p_clip_to ) {
// copy Latin-1 encoded c-string directly
2017-06-25 15:57:06 +02:00
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
2014-02-10 02:10:30 +01:00
int len = 0 ;
2020-07-27 12:43:20 +02:00
const char * ptr = p_cstr ;
2020-05-14 16:41:43 +02:00
while ( ( p_clip_to < 0 | | len < p_clip_to ) & & * ( ptr + + ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
len + + ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
resize ( len + 1 ) ; // include 0
2016-03-09 00:00:52 +01:00
2024-01-28 21:51:39 +01:00
char32_t * dst = ptrw ( ) ;
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
for ( int i = 0 ; i < len ; i + + ) {
2023-11-29 10:31:52 +01:00
# if CHAR_MIN == 0
uint8_t c = p_cstr [ i ] ;
# else
2022-07-05 14:18:29 +02:00
uint8_t c = p_cstr [ i ] > = 0 ? p_cstr [ i ] : uint8_t ( 256 + p_cstr [ i ] ) ;
2023-11-29 10:31:52 +01:00
# endif
2022-07-05 14:18:29 +02:00
if ( c = = 0 ) {
print_unicode_error ( " NUL character " , true ) ;
2023-03-11 10:31:41 +01:00
dst [ i ] = _replacement_char ;
2022-07-05 14:18:29 +02:00
} else {
dst [ i ] = c ;
}
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
dst [ len ] = 0 ;
}
void String : : 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 String : : copy_from ( const wchar_t * p_cstr , const 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
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char32_t & p_char ) {
2022-07-05 14:18:29 +02:00
if ( p_char = = 0 ) {
print_unicode_error ( " NUL character " , true ) ;
return ;
}
2023-03-11 10:31:41 +01:00
resize ( 2 ) ;
char32_t * dst = ptrw ( ) ;
2022-07-05 14:18:29 +02:00
if ( ( p_char & 0xfffff800 ) = = 0xd800 ) {
print_unicode_error ( vformat ( " Unpaired surrogate (%x) " , ( uint32_t ) p_char ) ) ;
2023-03-11 10:31:41 +01:00
dst [ 0 ] = _replacement_char ;
} else if ( p_char > 0x10ffff ) {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x) " , ( uint32_t ) p_char ) ) ;
2023-03-11 10:31:41 +01:00
dst [ 0 ] = _replacement_char ;
} else {
dst [ 0 ] = p_char ;
2022-07-05 14:18:29 +02:00
}
2022-02-21 22:14:28 +01:00
dst [ 1 ] = 0 ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char32_t * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
int len = 0 ;
const char32_t * ptr = p_cstr ;
while ( * ( ptr + + ) ! = 0 ) {
len + + ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
copy_from_unchecked ( p_cstr , len ) ;
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char32_t * p_cstr , const int p_clip_to ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
int len = 0 ;
const char32_t * ptr = p_cstr ;
while ( ( p_clip_to < 0 | | len < p_clip_to ) & & * ( ptr + + ) ! = 0 ) {
len + + ;
}
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
copy_from_unchecked ( p_cstr , len ) ;
}
// assumes the following have already been validated:
// p_char != nullptr
// p_length > 0
// p_length <= p_char strlen
void String : : copy_from_unchecked ( const char32_t * p_char , const int p_length ) {
resize ( p_length + 1 ) ;
char32_t * dst = ptrw ( ) ;
2022-02-21 22:14:28 +01:00
dst [ p_length ] = 0 ;
2020-07-27 12:43:20 +02:00
for ( int i = 0 ; i < p_length ; i + + ) {
2022-07-05 14:18:29 +02:00
if ( p_char [ i ] = = 0 ) {
print_unicode_error ( " NUL character " , true ) ;
2023-03-11 10:31:41 +01:00
dst [ i ] = _replacement_char ;
2022-07-05 14:18:29 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2022-07-05 14:18:29 +02:00
if ( ( p_char [ i ] & 0xfffff800 ) = = 0xd800 ) {
print_unicode_error ( vformat ( " Unpaired surrogate (%x) " , ( uint32_t ) p_char [ i ] ) ) ;
2023-03-11 10:31:41 +01:00
dst [ i ] = _replacement_char ;
continue ;
2022-07-05 14:18:29 +02:00
}
if ( p_char [ i ] > 0x10ffff ) {
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x) " , ( uint32_t ) p_char [ i ] ) ) ;
2023-03-11 10:31:41 +01:00
dst [ i ] = _replacement_char ;
continue ;
2022-07-05 14:18:29 +02:00
}
dst [ i ] = p_char [ i ] ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
void String : : operator = ( const char * p_str ) {
copy_from ( p_str ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
void String : : operator = ( const char32_t * p_str ) {
copy_from ( p_str ) ;
}
void String : : operator = ( const wchar_t * p_str ) {
copy_from ( p_str ) ;
2014-02-10 02:10:30 +01:00
}
String String : : operator + ( const String & p_str ) const {
String res = * this ;
res + = p_str ;
return res ;
}
2022-02-13 13:41:29 +01:00
String String : : operator + ( char32_t p_char ) const {
String res = * this ;
res + = p_char ;
return res ;
}
2020-07-27 12:43:20 +02:00
String operator + ( const char * p_chr , const String & p_str ) {
String tmp = p_chr ;
tmp + = p_str ;
return tmp ;
}
String operator + ( const wchar_t * p_chr , const String & p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
String tmp = String : : utf16 ( ( const char16_t * ) p_chr ) ;
# else
2022-07-05 14:18:29 +02:00
// wchar_t is 32-bit
2020-07-27 12:43:20 +02:00
String tmp = ( const char32_t * ) p_chr ;
# endif
tmp + = p_str ;
return tmp ;
}
String operator + ( char32_t p_chr , const String & p_str ) {
return ( String : : chr ( p_chr ) + p_str ) ;
}
2014-02-10 02:10:30 +01:00
String & String : : operator + = ( const String & p_str ) {
2022-02-21 22:14:28 +01:00
const int lhs_len = length ( ) ;
if ( lhs_len = = 0 ) {
2014-02-10 02:10:30 +01:00
* this = p_str ;
return * this ;
}
2016-03-09 00:00:52 +01:00
2022-02-21 22:14:28 +01:00
const int rhs_len = p_str . length ( ) ;
if ( rhs_len = = 0 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2022-02-21 22:14:28 +01:00
resize ( lhs_len + rhs_len + 1 ) ;
2016-03-09 00:00:52 +01:00
2022-08-18 14:24:14 +02:00
const char32_t * src = p_str . ptr ( ) ;
2022-02-21 22:14:28 +01:00
char32_t * dst = ptrw ( ) + lhs_len ;
2014-02-10 02:10:30 +01:00
2022-08-18 14:24:14 +02:00
// Don't copy the terminating null with `memcpy` to avoid undefined behavior when string is being added to itself (it would overlap the destination).
memcpy ( dst , src , rhs_len * sizeof ( char32_t ) ) ;
* ( dst + rhs_len ) = _null ;
2014-02-10 02:10:30 +01:00
return * this ;
}
String & String : : operator + = ( const char * p_str ) {
2020-05-14 16:41:43 +02:00
if ( ! p_str | | p_str [ 0 ] = = 0 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-02-21 22:14:28 +01:00
const int lhs_len = length ( ) ;
const size_t rhs_len = strlen ( p_str ) ;
2016-03-09 00:00:52 +01:00
2022-02-21 22:14:28 +01:00
resize ( lhs_len + rhs_len + 1 ) ;
2016-03-09 00:00:52 +01:00
2022-02-21 22:14:28 +01:00
char32_t * dst = ptrw ( ) + lhs_len ;
2016-03-09 00:00:52 +01:00
2022-02-21 22:14:28 +01:00
for ( size_t i = 0 ; i < = rhs_len ; i + + ) {
2023-11-29 10:31:52 +01:00
# if CHAR_MIN == 0
uint8_t c = p_str [ i ] ;
# else
2022-07-05 14:18:29 +02:00
uint8_t c = p_str [ i ] > = 0 ? p_str [ i ] : uint8_t ( 256 + p_str [ i ] ) ;
2023-11-29 10:31:52 +01:00
# endif
2022-07-05 14:18:29 +02:00
if ( c = = 0 & & i < rhs_len ) {
print_unicode_error ( " NUL character " , true ) ;
2023-03-11 10:31:41 +01:00
dst [ i ] = _replacement_char ;
2022-07-05 14:18:29 +02:00
} else {
dst [ i ] = c ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return * this ;
}
2020-07-27 12:43:20 +02:00
String & String : : operator + = ( const wchar_t * p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
* this + = String : : utf16 ( ( const char16_t * ) p_str ) ;
# else
// wchar_t is 32-bit
* this + = String ( ( const char32_t * ) p_str ) ;
# endif
return * this ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
String & String : : operator + = ( const char32_t * p_str ) {
* this + = String ( p_str ) ;
return * this ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
String & String : : operator + = ( char32_t p_char ) {
2022-07-05 14:18:29 +02:00
if ( p_char = = 0 ) {
print_unicode_error ( " NUL character " , true ) ;
return * this ;
}
2023-03-11 10:31:41 +01:00
const int lhs_len = length ( ) ;
resize ( lhs_len + 2 ) ;
char32_t * dst = ptrw ( ) ;
2022-07-05 14:18:29 +02:00
if ( ( p_char & 0xfffff800 ) = = 0xd800 ) {
print_unicode_error ( vformat ( " Unpaired surrogate (%x) " , ( uint32_t ) p_char ) ) ;
2023-03-11 10:31:41 +01:00
dst [ lhs_len ] = _replacement_char ;
} else if ( p_char > 0x10ffff ) {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x) " , ( uint32_t ) p_char ) ) ;
2023-03-11 10:31:41 +01:00
dst [ lhs_len ] = _replacement_char ;
} else {
dst [ lhs_len ] = p_char ;
2022-07-05 14:18:29 +02:00
}
2022-02-21 22:14:28 +01:00
dst [ lhs_len + 1 ] = 0 ;
2020-07-27 12:43:20 +02:00
return * this ;
}
bool String : : operator = = ( const char * p_str ) const {
// compare Latin-1 encoded c-string
int len = 0 ;
const char * aux = p_str ;
while ( * ( aux + + ) ! = 0 ) {
len + + ;
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( length ( ) ! = len ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
int l = length ( ) ;
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * dst = get_data ( ) ;
// Compare char by char
for ( int i = 0 ; i < l ; i + + ) {
if ( ( char32_t ) p_str [ i ] ! = dst [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool String : : operator = = ( const wchar_t * p_str ) const {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
return * this = = String : : utf16 ( ( const char16_t * ) p_str ) ;
# else
// wchar_t is 32-bit, compare char by char
return * this = = ( const char32_t * ) p_str ;
# endif
}
bool String : : operator = = ( const char32_t * p_str ) const {
2014-02-10 02:10:30 +01:00
int len = 0 ;
2020-07-27 12:43:20 +02:00
const char32_t * aux = p_str ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
while ( * ( aux + + ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
len + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( length ( ) ! = len ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
int l = length ( ) ;
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * dst = get_data ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/* Compare char by char */
for ( int i = 0 ; i < l ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( p_str [ i ] ! = dst [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool String : : operator = = ( const String & p_str ) const {
if ( length ( ) ! = p_str . length ( ) ) {
return false ;
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2020-07-27 12:43:20 +02:00
return true ;
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
int l = length ( ) ;
const char32_t * src = get_data ( ) ;
const char32_t * dst = p_str . get_data ( ) ;
/* Compare char by char */
for ( int i = 0 ; i < l ; i + + ) {
if ( src [ i ] ! = dst [ i ] ) {
return false ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
return true ;
}
bool String : : operator = = ( const StrRange & p_str_range ) const {
int len = p_str_range . len ;
2020-05-14 16:41:43 +02:00
if ( length ( ) ! = len ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * c_str = p_str_range . c_str ;
const char32_t * dst = & operator [ ] ( 0 ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/* Compare char by char */
2020-07-27 12:43:20 +02:00
for ( int i = 0 ; i < len ; i + + ) {
if ( c_str [ i ] ! = dst [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool operator = = ( const char * p_chr , const String & p_str ) {
return p_str = = p_chr ;
}
bool operator = = ( const wchar_t * p_chr , const String & p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return p_str = = String : : utf16 ( ( const char16_t * ) p_chr ) ;
# else
// wchar_t is 32-bi
return p_str = = String ( ( const char32_t * ) p_chr ) ;
# endif
}
2020-11-05 03:01:55 +01:00
bool operator ! = ( const char * p_chr , const String & p_str ) {
return ! ( p_str = = p_chr ) ;
}
bool operator ! = ( const wchar_t * p_chr , const String & p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return ! ( p_str = = String : : utf16 ( ( const char16_t * ) p_chr ) ) ;
# else
// wchar_t is 32-bi
return ! ( p_str = = String ( ( const char32_t * ) p_chr ) ) ;
# endif
}
2014-02-10 02:10:30 +01:00
bool String : : operator ! = ( const char * p_str ) const {
return ( ! ( * this = = p_str ) ) ;
}
2020-07-27 12:43:20 +02:00
bool String : : operator ! = ( const wchar_t * p_str ) const {
2014-02-10 02:10:30 +01:00
return ( ! ( * this = = p_str ) ) ;
}
2020-07-27 12:43:20 +02:00
bool String : : operator ! = ( const char32_t * p_str ) const {
return ( ! ( * this = = p_str ) ) ;
}
bool String : : operator ! = ( const String & p_str ) const {
return ! ( ( * this = = p_str ) ) ;
}
bool String : : operator < = ( const String & p_str ) const {
2020-11-05 03:01:55 +01:00
return ! ( p_str < * this ) ;
}
bool String : : operator > ( const String & p_str ) const {
return p_str < * this ;
}
2021-08-12 19:48:42 +02:00
2020-11-05 03:01:55 +01:00
bool String : : operator > = ( const String & p_str ) const {
return ! ( * this < p_str ) ;
2020-07-27 12:43:20 +02:00
}
bool String : : operator < ( const char * p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str [ 0 ] = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
return is_str_less ( get_data ( ) , p_str ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
bool String : : operator < ( const wchar_t * p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str [ 0 ] = = 0 ) {
2020-07-27 12:43:20 +02:00
return false ;
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2020-07-27 12:43:20 +02:00
return true ;
}
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return is_str_less ( get_data ( ) , String : : utf16 ( ( const char16_t * ) p_str ) . get_data ( ) ) ;
# else
// wchar_t is 32-bit
return is_str_less ( get_data ( ) , ( const char32_t * ) p_str ) ;
# endif
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
bool String : : operator < ( const char32_t * p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str [ 0 ] = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
return is_str_less ( get_data ( ) , p_str ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
bool String : : operator < ( const String & p_str ) const {
2020-07-27 12:43:20 +02:00
return operator < ( p_str . get_data ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
signed char String : : nocasecmp_to ( const String & p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * that_str = p_str . get_data ( ) ;
const char32_t * this_str = get_data ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( true ) {
2023-05-04 20:32:45 +02:00
if ( * that_str = = 0 & & * this_str = = 0 ) { // If both strings are at the end, they are equal.
return 0 ;
} else if ( * this_str = = 0 ) { // If at the end of this, and not of other, we are less.
2014-02-10 02:10:30 +01:00
return - 1 ;
2023-05-04 20:32:45 +02:00
} else if ( * that_str = = 0 ) { // If at end of other, and not of this, we are greater.
return 1 ;
} else if ( _find_upper ( * this_str ) < _find_upper ( * that_str ) ) { // If current character in this is less, we are less.
return - 1 ;
} else if ( _find_upper ( * this_str ) > _find_upper ( * that_str ) ) { // If current character in this is greater, we are greater.
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
this_str + + ;
that_str + + ;
}
}
2016-03-09 00:00:52 +01:00
signed char String : : casecmp_to ( const String & p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * that_str = p_str . get_data ( ) ;
const char32_t * this_str = get_data ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( true ) {
2023-05-04 20:32:45 +02:00
if ( * that_str = = 0 & & * this_str = = 0 ) { // If both strings are at the end, they are equal.
return 0 ;
} else if ( * this_str = = 0 ) { // If at the end of this, and not of other, we are less.
2014-02-10 02:10:30 +01:00
return - 1 ;
2023-05-04 20:32:45 +02:00
} else if ( * that_str = = 0 ) { // If at end of other, and not of this, we are greater.
return 1 ;
} else if ( * this_str < * that_str ) { // If current character in this is less, we are less.
return - 1 ;
} else if ( * this_str > * that_str ) { // If current character in this is greater, we are greater.
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
this_str + + ;
that_str + + ;
}
}
2023-05-04 20:32:45 +02:00
static _FORCE_INLINE_ signed char natural_cmp_common ( const char32_t * & r_this_str , const char32_t * & r_that_str ) {
// Keep ptrs to start of numerical sequences.
const char32_t * this_substr = r_this_str ;
const char32_t * that_substr = r_that_str ;
// Compare lengths of both numerical sequences, ignoring leading zeros.
while ( is_digit ( * r_this_str ) ) {
r_this_str + + ;
}
while ( is_digit ( * r_that_str ) ) {
r_that_str + + ;
}
while ( * this_substr = = ' 0 ' ) {
this_substr + + ;
}
while ( * that_substr = = ' 0 ' ) {
that_substr + + ;
}
int this_len = r_this_str - this_substr ;
int that_len = r_that_str - that_substr ;
if ( this_len < that_len ) {
return - 1 ;
} else if ( this_len > that_len ) {
return 1 ;
}
// If lengths equal, compare lexicographically.
while ( this_substr ! = r_this_str & & that_substr ! = r_that_str ) {
if ( * this_substr < * that_substr ) {
return - 1 ;
} else if ( * this_substr > * that_substr ) {
return 1 ;
}
this_substr + + ;
that_substr + + ;
}
return 0 ;
}
signed char String : : naturalcasecmp_to ( const String & p_str ) const {
2020-07-27 12:43:20 +02:00
const char32_t * this_str = get_data ( ) ;
const char32_t * that_str = p_str . get_data ( ) ;
2017-05-11 21:07:59 +02:00
if ( this_str & & that_str ) {
2017-05-29 04:56:40 +02:00
while ( * this_str = = ' . ' | | * that_str = = ' . ' ) {
2020-05-14 16:41:43 +02:00
if ( * this_str + + ! = ' . ' ) {
2017-05-29 04:56:40 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
if ( * that_str + + ! = ' . ' ) {
2017-05-29 04:56:40 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
if ( ! * that_str ) {
2017-05-29 04:56:40 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
if ( ! * this_str ) {
2017-05-29 04:56:40 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-29 04:56:40 +02:00
}
2017-05-11 21:07:59 +02:00
while ( * this_str ) {
2020-05-14 16:41:43 +02:00
if ( ! * that_str ) {
2017-05-11 21:07:59 +02:00
return 1 ;
2021-08-12 19:48:42 +02:00
} else if ( is_digit ( * this_str ) ) {
if ( ! is_digit ( * that_str ) ) {
2017-05-11 21:07:59 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-11 21:07:59 +02:00
2023-05-04 20:32:45 +02:00
signed char ret = natural_cmp_common ( this_str , that_str ) ;
if ( ret ) {
return ret ;
2020-05-14 16:41:43 +02:00
}
2023-05-04 20:32:45 +02:00
} else if ( is_digit ( * that_str ) ) {
return 1 ;
} else {
if ( * this_str < * that_str ) { // If current character in this is less, we are less.
return - 1 ;
} else if ( * this_str > * that_str ) { // If current character in this is greater, we are greater.
return 1 ;
2020-07-03 17:47:24 +02:00
}
2023-05-04 20:32:45 +02:00
this_str + + ;
that_str + + ;
}
}
if ( * that_str ) {
return - 1 ;
}
}
return 0 ;
}
signed char String : : naturalnocasecmp_to ( const String & p_str ) const {
const char32_t * this_str = get_data ( ) ;
const char32_t * that_str = p_str . get_data ( ) ;
if ( this_str & & that_str ) {
while ( * this_str = = ' . ' | | * that_str = = ' . ' ) {
if ( * this_str + + ! = ' . ' ) {
return 1 ;
}
if ( * that_str + + ! = ' . ' ) {
return - 1 ;
}
if ( ! * that_str ) {
return 1 ;
}
if ( ! * this_str ) {
return - 1 ;
}
}
while ( * this_str ) {
if ( ! * that_str ) {
return 1 ;
} else if ( is_digit ( * this_str ) ) {
if ( ! is_digit ( * that_str ) ) {
2020-07-03 17:47:24 +02:00
return - 1 ;
}
2023-05-04 20:32:45 +02:00
signed char ret = natural_cmp_common ( this_str , that_str ) ;
if ( ret ) {
return ret ;
2020-07-03 17:47:24 +02:00
}
2021-08-12 19:48:42 +02:00
} else if ( is_digit ( * that_str ) ) {
2017-05-11 21:07:59 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
} else {
2023-05-04 20:32:45 +02:00
if ( _find_upper ( * this_str ) < _find_upper ( * that_str ) ) { // If current character in this is less, we are less.
2017-05-11 21:07:59 +02:00
return - 1 ;
2023-05-04 20:32:45 +02:00
} else if ( _find_upper ( * this_str ) > _find_upper ( * that_str ) ) { // If current character in this is greater, we are greater.
2017-05-11 21:07:59 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-11 21:07:59 +02:00
this_str + + ;
that_str + + ;
}
}
2020-05-14 16:41:43 +02:00
if ( * that_str ) {
2017-05-11 21:07:59 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-11 21:07:59 +02:00
}
return 0 ;
}
2020-07-27 12:43:20 +02:00
const char32_t * String : : get_data ( ) const {
static const char32_t zero = 0 ;
return size ( ) ? & operator [ ] ( 0 ) : & zero ;
}
2022-08-30 11:36:24 +02:00
String String : : _camelcase_to_underscore ( ) const {
2020-07-27 12:43:20 +02:00
const char32_t * cstr = get_data ( ) ;
2016-02-17 19:26:22 +01:00
String new_string ;
int start_index = 0 ;
2015-03-23 06:08:07 +01:00
2024-01-28 21:51:39 +01:00
for ( int i = 1 ; i < size ( ) ; i + + ) {
2022-08-30 11:36:24 +02:00
bool is_prev_upper = is_ascii_upper_case ( cstr [ i - 1 ] ) ;
bool is_prev_lower = is_ascii_lower_case ( cstr [ i - 1 ] ) ;
bool is_prev_digit = is_digit ( cstr [ i - 1 ] ) ;
2021-08-12 19:48:42 +02:00
2022-08-30 11:36:24 +02:00
bool is_curr_upper = is_ascii_upper_case ( cstr [ i ] ) ;
bool is_curr_lower = is_ascii_lower_case ( cstr [ i ] ) ;
bool is_curr_digit = is_digit ( cstr [ i ] ) ;
2015-03-23 06:08:07 +01:00
2022-08-30 11:36:24 +02:00
bool is_next_lower = false ;
2024-01-28 21:51:39 +01:00
if ( i + 1 < size ( ) ) {
2022-02-04 09:32:20 +01:00
is_next_lower = is_ascii_lower_case ( cstr [ i + 1 ] ) ;
2018-09-30 06:33:21 +02:00
}
2022-08-30 11:36:24 +02:00
const bool cond_a = is_prev_lower & & is_curr_upper ; // aA
const bool cond_b = ( is_prev_upper | | is_prev_digit ) & & is_curr_upper & & is_next_lower ; // AAa, 2Aa
const bool cond_c = is_prev_digit & & is_curr_lower & & is_next_lower ; // 2aa
const bool cond_d = ( is_prev_upper | | is_prev_lower ) & & is_curr_digit ; // A2, a2
2018-09-30 06:33:21 +02:00
2022-08-30 11:36:24 +02:00
if ( cond_a | | cond_b | | cond_c | | cond_d ) {
2024-01-28 21:51:39 +01:00
new_string + = substr ( start_index , i - start_index ) + " _ " ;
2016-02-17 19:26:22 +01:00
start_index = i ;
}
}
2015-03-23 06:08:07 +01:00
2024-01-28 21:51:39 +01:00
new_string + = substr ( start_index , size ( ) - start_index ) ;
2022-08-30 11:36:24 +02:00
return new_string . to_lower ( ) ;
}
String String : : capitalize ( ) const {
2024-01-28 21:51:39 +01:00
String aux = _camelcase_to_underscore ( ) . replace ( " _ " , " " ) . strip_edges ( ) ;
2022-08-30 11:36:24 +02:00
String cap ;
for ( int i = 0 ; i < aux . get_slice_count ( " " ) ; i + + ) {
String slice = aux . get_slicec ( ' ' , i ) ;
if ( slice . length ( ) > 0 ) {
slice [ 0 ] = _find_upper ( slice [ 0 ] ) ;
if ( i > 0 ) {
cap + = " " ;
}
cap + = slice ;
}
}
return cap ;
}
String String : : to_camel_case ( ) const {
2024-01-28 21:51:39 +01:00
String s = to_pascal_case ( ) ;
2022-08-30 11:36:24 +02:00
if ( ! s . is_empty ( ) ) {
s [ 0 ] = _find_lower ( s [ 0 ] ) ;
}
return s ;
}
String String : : to_pascal_case ( ) const {
2024-01-28 21:51:39 +01:00
return capitalize ( ) . replace ( " " , " " ) ;
2022-08-30 11:36:24 +02:00
}
String String : : to_snake_case ( ) const {
2024-01-28 21:51:39 +01:00
return _camelcase_to_underscore ( ) . replace ( " " , " _ " ) . strip_edges ( ) ;
2015-03-23 06:08:07 +01:00
}
2019-06-16 04:45:24 +02:00
String String : : get_with_code_lines ( ) const {
2020-04-04 14:27:44 +02:00
const Vector < String > lines = split ( " \n " ) ;
2019-06-16 04:45:24 +02:00
String ret ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
if ( i > 0 ) {
ret + = " \n " ;
}
2020-04-04 14:27:44 +02:00
ret + = vformat ( " %4d | %s " , i + 1 , lines [ i ] ) ;
2019-06-16 04:45:24 +02:00
}
return ret ;
}
2020-05-14 14:29:06 +02:00
2023-11-02 18:36:57 +01:00
int String : : get_slice_count ( const String & p_splitter ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( p_splitter . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
int pos = 0 ;
int slices = 1 ;
while ( ( pos = find ( p_splitter , pos ) ) > = 0 ) {
slices + + ;
pos + = p_splitter . length ( ) ;
}
return slices ;
}
2023-11-02 18:36:57 +01:00
String String : : get_slice ( const String & p_splitter , int p_slice ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) | | p_splitter . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
int pos = 0 ;
int prev_pos = 0 ;
2017-01-14 12:26:56 +01:00
//int slices=1;
2020-05-14 16:41:43 +02:00
if ( p_slice < 0 ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
if ( find ( p_splitter ) = = - 1 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int i = 0 ;
while ( true ) {
pos = find ( p_splitter , pos ) ;
2020-05-14 16:41:43 +02:00
if ( pos = = - 1 ) {
2014-02-10 02:10:30 +01:00
pos = length ( ) ; //reached end
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int from = prev_pos ;
2017-01-14 12:26:56 +01:00
//int to=pos;
2014-02-10 02:10:30 +01:00
if ( p_slice = = i ) {
return substr ( from , pos - from ) ;
}
2020-05-14 16:41:43 +02:00
if ( pos = = length ( ) ) { //reached end and no find
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
pos + = p_splitter . length ( ) ;
prev_pos = pos ;
i + + ;
}
return " " ; //no find!
}
2020-07-27 12:43:20 +02:00
String String : : get_slicec ( char32_t p_splitter , int p_slice ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2015-06-29 05:29:49 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-06-29 05:29:49 +02:00
2020-05-14 16:41:43 +02:00
if ( p_slice < 0 ) {
2015-06-29 05:29:49 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-06-29 05:29:49 +02:00
2024-01-28 21:51:39 +01:00
const char32_t * c = ptr ( ) ;
2015-06-29 05:29:49 +02:00
int i = 0 ;
int prev = 0 ;
int count = 0 ;
while ( true ) {
if ( c [ i ] = = 0 | | c [ i ] = = p_splitter ) {
if ( p_slice = = count ) {
return substr ( prev , i - prev ) ;
2016-12-21 18:20:35 +01:00
} else if ( c [ i ] = = 0 ) {
return String ( ) ;
2015-06-29 05:29:49 +02:00
} else {
count + + ;
prev = i + 1 ;
}
}
i + + ;
}
}
2014-10-10 00:44:27 +02:00
Vector < String > String : : split_spaces ( ) const {
Vector < String > ret ;
int from = 0 ;
int i = 0 ;
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-01-15 22:40:41 +01:00
return ret ;
2020-05-14 16:41:43 +02:00
}
2018-01-15 22:40:41 +01:00
2014-10-10 00:44:27 +02:00
bool inside = false ;
while ( true ) {
bool empty = operator [ ] ( i ) < 33 ;
2020-05-14 16:41:43 +02:00
if ( i = = 0 ) {
2014-10-10 00:44:27 +02:00
inside = ! empty ;
2020-05-14 16:41:43 +02:00
}
2014-10-10 00:44:27 +02:00
if ( ! empty & & ! inside ) {
inside = true ;
from = i ;
}
if ( empty & & inside ) {
ret . push_back ( substr ( from , i - from ) ) ;
inside = false ;
}
2020-05-14 16:41:43 +02:00
if ( i = = len ) {
2014-10-10 00:44:27 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-10-10 00:44:27 +02:00
i + + ;
}
return ret ;
}
2017-12-12 03:14:38 +01:00
Vector < String > String : : split ( const String & p_splitter , bool p_allow_empty , int p_maxsplit ) const {
2014-02-10 02:10:30 +01:00
Vector < String > ret ;
2023-02-01 23:29:17 +01:00
if ( is_empty ( ) ) {
if ( p_allow_empty ) {
ret . push_back ( " " ) ;
}
return ret ;
}
2014-02-10 02:10:30 +01:00
int from = 0 ;
int len = length ( ) ;
while ( true ) {
2022-08-12 16:21:15 +02:00
int end ;
if ( p_splitter . is_empty ( ) ) {
end = from + 1 ;
} else {
end = find ( p_splitter , from ) ;
if ( end < 0 ) {
end = len ;
}
2020-05-14 16:41:43 +02:00
}
2017-12-12 03:14:38 +01:00
if ( p_allow_empty | | ( end > from ) ) {
2020-05-14 16:41:43 +02:00
if ( p_maxsplit < = 0 ) {
2017-12-12 03:14:38 +01:00
ret . push_back ( substr ( from , end - from ) ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-12-12 03:14:38 +01:00
// Put rest of the string and leave cycle.
if ( p_maxsplit = = ret . size ( ) ) {
ret . push_back ( substr ( from , len ) ) ;
break ;
}
// Otherwise, push items until positive limit is reached.
ret . push_back ( substr ( from , end - from ) ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
from = end + p_splitter . length ( ) ;
}
return ret ;
}
2017-12-15 20:23:58 +01:00
Vector < String > String : : rsplit ( const String & p_splitter , bool p_allow_empty , int p_maxsplit ) const {
Vector < String > ret ;
const int len = length ( ) ;
2018-07-17 20:45:29 +02:00
int remaining_len = len ;
2017-12-15 20:23:58 +01:00
while ( true ) {
2018-07-17 20:45:29 +02:00
if ( remaining_len < p_splitter . length ( ) | | ( p_maxsplit > 0 & & p_maxsplit = = ret . size ( ) ) ) {
// no room for another splitter or hit max splits, push what's left and we're done
if ( p_allow_empty | | remaining_len > 0 ) {
ret . push_back ( substr ( 0 , remaining_len ) ) ;
2017-12-15 20:23:58 +01:00
}
2018-07-17 20:45:29 +02:00
break ;
2017-12-15 20:23:58 +01:00
}
2022-08-12 16:21:15 +02:00
int left_edge ;
if ( p_splitter . is_empty ( ) ) {
left_edge = remaining_len - 1 ;
if ( left_edge = = 0 ) {
left_edge - - ; // Skip to the < 0 condition.
}
} else {
left_edge = rfind ( p_splitter , remaining_len - p_splitter . length ( ) ) ;
}
2018-07-17 20:45:29 +02:00
if ( left_edge < 0 ) {
// no more splitters, we're done
ret . push_back ( substr ( 0 , remaining_len ) ) ;
2017-12-15 20:23:58 +01:00
break ;
2018-07-17 20:45:29 +02:00
}
int substr_start = left_edge + p_splitter . length ( ) ;
if ( p_allow_empty | | substr_start < remaining_len ) {
ret . push_back ( substr ( substr_start , remaining_len - substr_start ) ) ;
}
2017-12-15 20:23:58 +01:00
2018-07-17 20:45:29 +02:00
remaining_len = left_edge ;
2017-12-15 20:23:58 +01:00
}
2021-03-14 08:21:32 +01:00
ret . reverse ( ) ;
2017-12-15 20:23:58 +01:00
return ret ;
}
2022-11-20 12:29:50 +01:00
Vector < double > String : : split_floats ( const String & p_splitter , bool p_allow_empty ) const {
Vector < double > ret ;
2014-02-10 02:10:30 +01:00
int from = 0 ;
int len = length ( ) ;
while ( true ) {
int end = find ( p_splitter , from ) ;
2020-05-14 16:41:43 +02:00
if ( end < 0 ) {
2014-02-10 02:10:30 +01:00
end = len ;
2020-05-14 16:41:43 +02:00
}
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_float ( & get_data ( ) [ from ] ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
from = end + p_splitter . length ( ) ;
}
return ret ;
}
Vector < float > String : : split_floats_mk ( const Vector < String > & p_splitters , bool p_allow_empty ) const {
Vector < float > ret ;
int from = 0 ;
int len = length ( ) ;
while ( true ) {
int idx ;
int end = findmk ( p_splitters , from , & idx ) ;
int spl_len = 1 ;
if ( end < 0 ) {
end = len ;
} else {
spl_len = p_splitters [ idx ] . length ( ) ;
}
2015-01-03 15:06:53 +01:00
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_float ( & get_data ( ) [ from ] ) ) ;
2015-01-03 15:06:53 +01:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
from = end + spl_len ;
}
return ret ;
}
Vector < int > String : : split_ints ( const String & p_splitter , bool p_allow_empty ) const {
Vector < int > ret ;
int from = 0 ;
int len = length ( ) ;
while ( true ) {
int end = find ( p_splitter , from ) ;
2020-05-14 16:41:43 +02:00
if ( end < 0 ) {
2014-02-10 02:10:30 +01:00
end = len ;
2020-05-14 16:41:43 +02:00
}
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_int ( & get_data ( ) [ from ] , end - from ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
from = end + p_splitter . length ( ) ;
}
return ret ;
}
Vector < int > String : : split_ints_mk ( const Vector < String > & p_splitters , bool p_allow_empty ) const {
Vector < int > ret ;
int from = 0 ;
int len = length ( ) ;
while ( true ) {
int idx ;
int end = findmk ( p_splitters , from , & idx ) ;
int spl_len = 1 ;
if ( end < 0 ) {
end = len ;
} else {
spl_len = p_splitters [ idx ] . length ( ) ;
}
2020-05-14 16:41:43 +02:00
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_int ( & get_data ( ) [ from ] , end - from ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
from = end + spl_len ;
}
return ret ;
}
2020-07-27 12:43:20 +02:00
String String : : join ( Vector < String > parts ) const {
2017-10-02 17:01:43 +02:00
String ret ;
for ( int i = 0 ; i < parts . size ( ) ; + + i ) {
if ( i > 0 ) {
ret + = * this ;
}
ret + = parts [ i ] ;
}
return ret ;
}
2020-07-27 12:43:20 +02:00
char32_t String : : char_uppercase ( char32_t p_char ) {
2014-02-10 02:10:30 +01:00
return _find_upper ( p_char ) ;
}
2020-07-27 12:43:20 +02:00
char32_t String : : char_lowercase ( char32_t p_char ) {
2014-02-10 02:10:30 +01:00
return _find_lower ( p_char ) ;
}
String String : : to_upper ( ) const {
String upper = * this ;
for ( int i = 0 ; i < upper . size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
const char32_t s = upper [ i ] ;
const char32_t t = _find_upper ( s ) ;
2020-05-14 16:41:43 +02:00
if ( s ! = t ) { // avoid copy on write
2017-12-11 23:38:07 +01:00
upper [ i ] = t ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return upper ;
}
String String : : to_lower ( ) const {
2017-12-11 23:38:07 +01:00
String lower = * this ;
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
for ( int i = 0 ; i < lower . size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
const char32_t s = lower [ i ] ;
const char32_t t = _find_lower ( s ) ;
2020-05-14 16:41:43 +02:00
if ( s ! = t ) { // avoid copy on write
2017-12-11 23:38:07 +01:00
lower [ i ] = t ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
return lower ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
String String : : chr ( char32_t p_char ) {
char32_t c [ 2 ] = { p_char , 0 } ;
2014-02-10 02:10:30 +01:00
return String ( c ) ;
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
String String : : num ( double p_num , int p_decimals ) {
2020-05-20 21:49:39 +02:00
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
2020-07-27 12:43:20 +02:00
if ( Math : : is_inf ( p_num ) ) {
if ( signbit ( p_num ) ) {
return " -inf " ;
} else {
return " inf " ;
}
}
2014-02-10 02:10:30 +01:00
2019-12-23 16:38:33 +01:00
if ( p_decimals < 0 ) {
2021-08-17 11:43:11 +02:00
p_decimals = 14 ;
2023-09-05 08:31:29 +02:00
const double abs_num = Math : : abs ( p_num ) ;
2021-08-17 11:43:11 +02:00
if ( abs_num > 10 ) {
2023-09-05 08:31:29 +02:00
// We want to align the digits to the above reasonable default, so we only
2021-08-17 11:43:11 +02:00
// need to subtract log10 for numbers with a positive power of ten.
p_decimals - = ( int ) floor ( log10 ( abs_num ) ) ;
}
2019-12-23 16:38:33 +01:00
}
if ( p_decimals > MAX_DECIMALS ) {
p_decimals = MAX_DECIMALS ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
char fmt [ 7 ] ;
fmt [ 0 ] = ' % ' ;
fmt [ 1 ] = ' . ' ;
if ( p_decimals < 0 ) {
fmt [ 1 ] = ' l ' ;
fmt [ 2 ] = ' f ' ;
fmt [ 3 ] = 0 ;
} else if ( p_decimals < 10 ) {
fmt [ 2 ] = ' 0 ' + p_decimals ;
fmt [ 3 ] = ' l ' ;
fmt [ 4 ] = ' f ' ;
fmt [ 5 ] = 0 ;
} else {
fmt [ 2 ] = ' 0 ' + ( p_decimals / 10 ) ;
fmt [ 3 ] = ' 0 ' + ( p_decimals % 10 ) ;
fmt [ 4 ] = ' l ' ;
fmt [ 5 ] = ' f ' ;
fmt [ 6 ] = 0 ;
}
2022-10-11 13:15:51 +02:00
// if we want to convert a double with as much decimal places as as
// DBL_MAX or DBL_MIN then we would theoretically need a buffer of at least
// DBL_MAX_10_EXP + 2 for DBL_MAX and DBL_MAX_10_EXP + 4 for DBL_MIN.
// BUT those values where still giving me exceptions, so I tested from
// DBL_MAX_10_EXP + 10 incrementing one by one and DBL_MAX_10_EXP + 17 (325)
// was the first buffer size not to throw an exception
char buf [ 325 ] ;
2014-02-10 02:10:30 +01:00
2015-11-20 00:19:44 +01:00
# if defined(__GNUC__) || defined(_MSC_VER)
2022-10-11 13:15:51 +02:00
// PLEASE NOTE that, albeit vcrt online reference states that snprintf
// should safely truncate the output to the given buffer size, we have
// found a case where this is not true, so we should create a buffer
// as big as needed
snprintf ( buf , 325 , fmt , p_num ) ;
2014-02-10 02:10:30 +01:00
# else
sprintf ( buf , fmt , p_num ) ;
# endif
2022-10-11 13:15:51 +02:00
buf [ 324 ] = 0 ;
2014-02-10 02:10:30 +01:00
//destroy trailing zeroes
{
bool period = false ;
int z = 0 ;
while ( buf [ z ] ) {
2020-05-14 16:41:43 +02:00
if ( buf [ z ] = = ' . ' ) {
2014-02-10 02:10:30 +01:00
period = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
z + + ;
}
if ( period ) {
z - - ;
while ( z > 0 ) {
if ( buf [ z ] = = ' 0 ' ) {
buf [ z ] = 0 ;
} else if ( buf [ z ] = = ' . ' ) {
buf [ z ] = 0 ;
break ;
} else {
break ;
}
z - - ;
}
}
}
return buf ;
}
2015-02-01 19:42:36 +01:00
String String : : num_int64 ( int64_t p_num , int base , bool capitalize_hex ) {
2014-02-10 02:10:30 +01:00
bool sign = p_num < 0 ;
2018-01-13 21:04:30 +01:00
int64_t n = p_num ;
2014-02-10 02:10:30 +01:00
int chars = 0 ;
do {
2015-02-01 19:42:36 +01:00
n / = base ;
2014-02-10 02:10:30 +01:00
chars + + ;
} while ( n ) ;
2020-05-14 16:41:43 +02:00
if ( sign ) {
2014-02-10 02:10:30 +01:00
chars + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String s ;
s . resize ( chars + 1 ) ;
2020-07-27 12:43:20 +02:00
char32_t * c = s . ptrw ( ) ;
2014-02-10 02:10:30 +01:00
c [ chars ] = 0 ;
2018-01-13 21:04:30 +01:00
n = p_num ;
2014-02-10 02:10:30 +01:00
do {
2018-01-13 21:04:30 +01:00
int mod = ABS ( n % base ) ;
2015-02-01 19:42:36 +01:00
if ( mod > = 10 ) {
char a = ( capitalize_hex ? ' A ' : ' a ' ) ;
c [ - - chars ] = a + ( mod - 10 ) ;
} else {
c [ - - chars ] = ' 0 ' + mod ;
}
n / = base ;
2014-02-10 02:10:30 +01:00
} while ( n ) ;
2020-05-14 16:41:43 +02:00
if ( sign ) {
2014-02-10 02:10:30 +01:00
c [ 0 ] = ' - ' ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return s ;
}
2018-02-22 13:13:51 +01:00
String String : : num_uint64 ( uint64_t p_num , int base , bool capitalize_hex ) {
uint64_t n = p_num ;
int chars = 0 ;
do {
n / = base ;
chars + + ;
} while ( n ) ;
String s ;
s . resize ( chars + 1 ) ;
2020-07-27 12:43:20 +02:00
char32_t * c = s . ptrw ( ) ;
2018-02-22 13:13:51 +01:00
c [ chars ] = 0 ;
n = p_num ;
do {
2018-04-18 22:20:39 +02:00
int mod = n % base ;
2018-02-22 13:13:51 +01:00
if ( mod > = 10 ) {
char a = ( capitalize_hex ? ' A ' : ' a ' ) ;
c [ - - chars ] = a + ( mod - 10 ) ;
} else {
c [ - - chars ] = ' 0 ' + mod ;
}
n / = base ;
} while ( n ) ;
return s ;
}
2021-02-25 15:54:50 +01:00
String String : : num_real ( double p_num , bool p_trailing ) {
2022-01-15 04:17:01 +01:00
if ( p_num = = ( double ) ( int64_t ) p_num ) {
if ( p_trailing ) {
return num_int64 ( ( int64_t ) p_num ) + " .0 " ;
2020-07-27 12:43:20 +02:00
} else {
2022-01-15 04:17:01 +01:00
return num_int64 ( ( int64_t ) p_num ) ;
2020-07-27 12:43:20 +02:00
}
}
2021-08-19 00:49:37 +02:00
# ifdef REAL_T_IS_DOUBLE
2022-01-15 04:17:01 +01:00
int decimals = 14 ;
2019-12-23 16:38:33 +01:00
# else
2022-01-15 04:17:01 +01:00
int decimals = 6 ;
2019-12-23 16:38:33 +01:00
# endif
2022-10-16 00:36:22 +02:00
// We want to align the digits to the above sane default, so we only need
// to subtract log10 for numbers with a positive power of ten magnitude.
double abs_num = Math : : abs ( p_num ) ;
if ( abs_num > 10 ) {
decimals - = ( int ) floor ( log10 ( abs_num ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-01-15 04:17:01 +01:00
return num ( p_num , decimals ) ;
2014-02-10 02:10:30 +01:00
}
String String : : num_scientific ( double p_num ) {
2020-05-20 21:49:39 +02:00
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
2020-07-27 12:43:20 +02:00
if ( Math : : is_inf ( p_num ) ) {
if ( signbit ( p_num ) ) {
return " -inf " ;
} else {
return " inf " ;
}
}
2014-02-10 02:10:30 +01:00
char buf [ 256 ] ;
2015-11-20 00:19:44 +01:00
# if defined(__GNUC__) || defined(_MSC_VER)
2019-11-05 22:04:04 +01:00
2021-08-31 20:02:55 +02:00
# if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
// MinGW requires _set_output_format() to conform to C99 output for printf
2019-11-05 22:04:04 +01:00
unsigned int old_exponent_format = _set_output_format ( _TWO_DIGIT_EXPONENT ) ;
# endif
2014-02-10 02:10:30 +01:00
snprintf ( buf , 256 , " %lg " , p_num ) ;
2019-11-05 22:04:04 +01:00
2021-08-31 20:02:55 +02:00
# if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
2019-11-05 22:04:04 +01:00
_set_output_format ( old_exponent_format ) ;
# endif
2014-02-10 02:10:30 +01:00
# else
sprintf ( buf , " %.16lg " , p_num ) ;
# endif
buf [ 255 ] = 0 ;
return buf ;
}
2020-07-27 12:43:20 +02:00
String String : : md5 ( const uint8_t * p_md5 ) {
return String : : hex_encode_buffer ( p_md5 , 16 ) ;
}
String String : : hex_encode_buffer ( const uint8_t * p_buffer , int p_len ) {
static const char hex [ 16 ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' , ' a ' , ' b ' , ' c ' , ' d ' , ' e ' , ' f ' } ;
String ret ;
char v [ 2 ] = { 0 , 0 } ;
for ( int i = 0 ; i < p_len ; i + + ) {
v [ 0 ] = hex [ p_buffer [ i ] > > 4 ] ;
ret + = v ;
v [ 0 ] = hex [ p_buffer [ i ] & 0xF ] ;
ret + = v ;
}
return ret ;
}
2023-03-06 04:17:33 +01:00
Vector < uint8_t > String : : hex_decode ( ) const {
ERR_FAIL_COND_V_MSG ( length ( ) % 2 ! = 0 , Vector < uint8_t > ( ) , " Hexadecimal string of uneven length. " ) ;
# define HEX_TO_BYTE(m_output, m_index) \
uint8_t m_output ; \
c = operator [ ] ( m_index ) ; \
if ( is_digit ( c ) ) { \
m_output = c - ' 0 ' ; \
} else if ( c > = ' a ' & & c < = ' f ' ) { \
m_output = c - ' a ' + 10 ; \
} else if ( c > = ' A ' & & c < = ' F ' ) { \
m_output = c - ' A ' + 10 ; \
} else { \
ERR_FAIL_V_MSG ( Vector < uint8_t > ( ) , " Invalid hexadecimal character \" " + chr ( c ) + " \" at index " + m_index + " . " ) ; \
}
Vector < uint8_t > out ;
int len = length ( ) / 2 ;
out . resize ( len ) ;
for ( int i = 0 ; i < len ; i + + ) {
char32_t c ;
HEX_TO_BYTE ( first , i * 2 ) ;
HEX_TO_BYTE ( second , i * 2 + 1 ) ;
out . write [ i ] = first * 16 + second ;
}
return out ;
# undef HEX_TO_BYTE
}
2022-07-05 14:18:29 +02:00
void String : : print_unicode_error ( const String & p_message , bool p_critical ) const {
if ( p_critical ) {
2023-07-28 11:19:05 +02:00
print_error ( vformat ( U " Unicode parsing error, some characters were replaced with <20> (U+FFFD): %s " , p_message ) ) ;
2022-07-05 14:18:29 +02:00
} else {
print_error ( vformat ( " Unicode parsing error: %s " , p_message ) ) ;
}
}
2014-02-10 02:10:30 +01:00
CharString String : : ascii ( bool p_allow_extended ) const {
2020-05-14 16:41:43 +02:00
if ( ! length ( ) ) {
2014-02-10 02:10:30 +01:00
return CharString ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
CharString cs ;
cs . resize ( size ( ) ) ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
if ( ( c < = 0x7f ) | | ( c < = 0xff & & p_allow_extended ) ) {
cs [ i ] = c ;
} else {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1 " , ( uint32_t ) c ) ) ;
2023-03-11 10:31:41 +01:00
cs [ i ] = 0x20 ; // ascii doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane
2020-07-27 12:43:20 +02:00
}
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return cs ;
}
String String : : utf8 ( const char * p_utf8 , int p_len ) {
String ret ;
ret . parse_utf8 ( p_utf8 , p_len ) ;
return ret ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2022-07-31 18:46:53 +02:00
Error String : : parse_utf8 ( const char * p_utf8 , int p_len , bool p_skip_cr ) {
2020-05-14 16:41:43 +02:00
if ( ! p_utf8 ) {
2022-07-05 14:18:29 +02:00
return ERR_INVALID_DATA ;
2020-05-14 16:41:43 +02:00
}
2019-02-22 19:27:13 +01:00
2014-02-10 02:10:30 +01:00
String aux ;
int cstr_size = 0 ;
int str_size = 0 ;
/* HANDLE BOM (Byte Order Mark) */
if ( p_len < 0 | | p_len > = 3 ) {
2020-07-27 12:43:20 +02:00
bool has_bom = uint8_t ( p_utf8 [ 0 ] ) = = 0xef & & uint8_t ( p_utf8 [ 1 ] ) = = 0xbb & & uint8_t ( p_utf8 [ 2 ] ) = = 0xbf ;
2014-02-10 02:10:30 +01:00
if ( has_bom ) {
2020-07-27 12:43:20 +02:00
//8-bit encoding, byte order has no meaning in UTF-8, just skip it
2020-05-14 16:41:43 +02:00
if ( p_len > = 0 ) {
2014-02-10 02:10:30 +01:00
p_len - = 3 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
p_utf8 + = 3 ;
}
}
2022-07-05 14:18:29 +02:00
bool decode_error = false ;
bool decode_failed = false ;
2014-02-10 02:10:30 +01:00
{
const char * ptrtmp = p_utf8 ;
const char * ptrtmp_limit = & p_utf8 [ p_len ] ;
int skip = 0 ;
2022-07-05 14:18:29 +02:00
uint8_t c_start = 0 ;
2014-02-10 02:10:30 +01:00
while ( ptrtmp ! = ptrtmp_limit & & * ptrtmp ) {
2023-11-29 10:31:52 +01:00
# if CHAR_MIN == 0
uint8_t c = * ptrtmp ;
# else
2022-07-05 14:18:29 +02:00
uint8_t c = * ptrtmp > = 0 ? * ptrtmp : uint8_t ( 256 + * ptrtmp ) ;
2023-11-29 10:31:52 +01:00
# endif
2014-02-10 02:10:30 +01:00
2022-07-05 14:18:29 +02:00
if ( skip = = 0 ) {
2022-07-31 18:46:53 +02:00
if ( p_skip_cr & & c = = ' \r ' ) {
ptrtmp + + ;
continue ;
}
2014-02-10 02:10:30 +01:00
/* Determine the number of characters in sequence */
2020-05-14 16:41:43 +02:00
if ( ( c & 0x80 ) = = 0 ) {
2014-02-10 02:10:30 +01:00
skip = 0 ;
2020-07-27 12:43:20 +02:00
} else if ( ( c & 0xe0 ) = = 0xc0 ) {
2014-02-10 02:10:30 +01:00
skip = 1 ;
2020-07-27 12:43:20 +02:00
} else if ( ( c & 0xf0 ) = = 0xe0 ) {
2014-02-10 02:10:30 +01:00
skip = 2 ;
2020-07-27 12:43:20 +02:00
} else if ( ( c & 0xf8 ) = = 0xf0 ) {
2014-02-10 02:10:30 +01:00
skip = 3 ;
2022-07-05 14:18:29 +02:00
} else if ( ( c & 0xfc ) = = 0xf8 ) {
skip = 4 ;
} else if ( ( c & 0xfe ) = = 0xfc ) {
skip = 5 ;
2020-05-14 16:41:43 +02:00
} else {
2022-07-05 14:18:29 +02:00
skip = 0 ;
print_unicode_error ( vformat ( " Invalid UTF-8 leading byte (%x) " , c ) , true ) ;
decode_failed = true ;
2014-02-10 02:10:30 +01:00
}
2022-07-05 14:18:29 +02:00
c_start = c ;
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
if ( skip = = 1 & & ( c & 0x1e ) = = 0 ) {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Overlong encoding (%x ...) " , c ) ) ;
decode_error = true ;
2014-02-10 02:10:30 +01:00
}
str_size + + ;
} else {
2022-07-05 14:18:29 +02:00
if ( ( c_start = = 0xe0 & & skip = = 2 & & c < 0xa0 ) | | ( c_start = = 0xf0 & & skip = = 3 & & c < 0x90 ) | | ( c_start = = 0xf8 & & skip = = 4 & & c < 0x88 ) | | ( c_start = = 0xfc & & skip = = 5 & & c < 0x84 ) ) {
print_unicode_error ( vformat ( " Overlong encoding (%x %x ...) " , c_start , c ) ) ;
decode_error = true ;
}
if ( c < 0x80 | | c > 0xbf ) {
print_unicode_error ( vformat ( " Invalid UTF-8 continuation byte (%x ... %x ...) " , c_start , c ) , true ) ;
decode_failed = true ;
skip = 0 ;
} else {
- - skip ;
}
2014-02-10 02:10:30 +01:00
}
cstr_size + + ;
ptrtmp + + ;
}
if ( skip ) {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Missing %d UTF-8 continuation byte(s) " , skip ) , true ) ;
decode_failed = true ;
2014-02-10 02:10:30 +01:00
}
}
if ( str_size = = 0 ) {
clear ( ) ;
2022-07-05 14:18:29 +02:00
return OK ; // empty string
2014-02-10 02:10:30 +01:00
}
resize ( str_size + 1 ) ;
2020-07-27 12:43:20 +02:00
char32_t * dst = ptrw ( ) ;
2014-02-10 02:10:30 +01:00
dst [ str_size ] = 0 ;
2022-07-05 14:18:29 +02:00
int skip = 0 ;
uint32_t unichar = 0 ;
2014-02-10 02:10:30 +01:00
while ( cstr_size ) {
2023-11-29 10:31:52 +01:00
# if CHAR_MIN == 0
uint8_t c = * p_utf8 ;
# else
2022-07-05 14:18:29 +02:00
uint8_t c = * p_utf8 > = 0 ? * p_utf8 : uint8_t ( 256 + * p_utf8 ) ;
2023-11-29 10:31:52 +01:00
# endif
2022-07-05 14:18:29 +02:00
if ( skip = = 0 ) {
2022-07-31 18:46:53 +02:00
if ( p_skip_cr & & c = = ' \r ' ) {
p_utf8 + + ;
continue ;
}
2022-07-05 14:18:29 +02:00
/* Determine the number of characters in sequence */
if ( ( c & 0x80 ) = = 0 ) {
* ( dst + + ) = c ;
unichar = 0 ;
skip = 0 ;
} else if ( ( c & 0xe0 ) = = 0xc0 ) {
unichar = ( 0xff > > 3 ) & c ;
skip = 1 ;
} else if ( ( c & 0xf0 ) = = 0xe0 ) {
unichar = ( 0xff > > 4 ) & c ;
skip = 2 ;
} else if ( ( c & 0xf8 ) = = 0xf0 ) {
unichar = ( 0xff > > 5 ) & c ;
skip = 3 ;
} else if ( ( c & 0xfc ) = = 0xf8 ) {
unichar = ( 0xff > > 6 ) & c ;
skip = 4 ;
} else if ( ( c & 0xfe ) = = 0xfc ) {
unichar = ( 0xff > > 7 ) & c ;
skip = 5 ;
} else {
2023-03-11 10:31:41 +01:00
* ( dst + + ) = _replacement_char ;
2022-07-05 14:18:29 +02:00
unichar = 0 ;
skip = 0 ;
}
2020-05-14 16:41:43 +02:00
} else {
2022-07-05 14:18:29 +02:00
if ( c < 0x80 | | c > 0xbf ) {
2023-03-11 10:31:41 +01:00
* ( dst + + ) = _replacement_char ;
2022-07-05 14:18:29 +02:00
skip = 0 ;
} else {
unichar = ( unichar < < 6 ) | ( c & 0x3f ) ;
- - skip ;
if ( skip = = 0 ) {
if ( unichar = = 0 ) {
print_unicode_error ( " NUL character " , true ) ;
decode_failed = true ;
2023-03-11 10:31:41 +01:00
unichar = _replacement_char ;
} else if ( ( unichar & 0xfffff800 ) = = 0xd800 ) {
print_unicode_error ( vformat ( " Unpaired surrogate (%x) " , unichar ) , true ) ;
decode_failed = true ;
unichar = _replacement_char ;
} else if ( unichar > 0x10ffff ) {
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x) " , unichar ) , true ) ;
decode_failed = true ;
unichar = _replacement_char ;
2022-07-05 14:18:29 +02:00
}
* ( dst + + ) = unichar ;
2014-02-10 02:10:30 +01:00
}
}
}
2022-07-05 14:18:29 +02:00
cstr_size - - ;
p_utf8 + + ;
}
if ( skip ) {
* ( dst + + ) = 0x20 ;
2014-02-10 02:10:30 +01:00
}
2022-07-05 14:18:29 +02:00
if ( decode_failed ) {
return ERR_INVALID_DATA ;
} else if ( decode_error ) {
return ERR_PARSE_ERROR ;
} else {
return OK ;
}
2014-02-10 02:10:30 +01:00
}
CharString String : : utf8 ( ) const {
int l = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! l ) {
2014-02-10 02:10:30 +01:00
return CharString ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * d = & operator [ ] ( 0 ) ;
2014-02-10 02:10:30 +01:00
int fl = 0 ;
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
2020-05-14 16:41:43 +02:00
if ( c < = 0x7f ) { // 7 bits.
2014-02-10 02:10:30 +01:00
fl + = 1 ;
2020-05-14 16:41:43 +02:00
} else if ( c < = 0x7ff ) { // 11 bits
2014-02-10 02:10:30 +01:00
fl + = 2 ;
} else if ( c < = 0xffff ) { // 16 bits
fl + = 3 ;
2022-07-05 14:18:29 +02:00
} else if ( c < = 0x001fffff ) { // 21 bits
2014-02-10 02:10:30 +01:00
fl + = 4 ;
2022-07-05 14:18:29 +02:00
} else if ( c < = 0x03ffffff ) { // 26 bits
fl + = 5 ;
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x) " , c ) ) ;
} else if ( c < = 0x7fffffff ) { // 31 bits
fl + = 6 ;
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x) " , c ) ) ;
2020-07-27 12:43:20 +02:00
} else {
2022-07-05 14:18:29 +02:00
fl + = 1 ;
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x), cannot represent as UTF-8 " , c ) , true ) ;
2014-02-10 02:10:30 +01:00
}
}
CharString utf8s ;
if ( fl = = 0 ) {
return utf8s ;
}
utf8s . resize ( fl + 1 ) ;
uint8_t * cdst = ( uint8_t * ) utf8s . get_data ( ) ;
# define APPEND_CHAR(m_c) *(cdst++) = m_c
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
2020-05-14 16:41:43 +02:00
if ( c < = 0x7f ) { // 7 bits.
2014-02-10 02:10:30 +01:00
APPEND_CHAR ( c ) ;
2020-05-14 16:41:43 +02:00
} else if ( c < = 0x7ff ) { // 11 bits
2014-02-10 02:10:30 +01:00
APPEND_CHAR ( uint32_t ( 0xc0 | ( ( c > > 6 ) & 0x1f ) ) ) ; // Top 5 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
} else if ( c < = 0xffff ) { // 16 bits
APPEND_CHAR ( uint32_t ( 0xe0 | ( ( c > > 12 ) & 0x0f ) ) ) ; // Top 4 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 6 ) & 0x3f ) ) ) ; // Middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
2022-07-05 14:18:29 +02:00
} else if ( c < = 0x001fffff ) { // 21 bits
2014-02-10 02:10:30 +01:00
APPEND_CHAR ( uint32_t ( 0xf0 | ( ( c > > 18 ) & 0x07 ) ) ) ; // Top 3 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 12 ) & 0x3f ) ) ) ; // Upper middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 6 ) & 0x3f ) ) ) ; // Lower middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
2022-07-05 14:18:29 +02:00
} else if ( c < = 0x03ffffff ) { // 26 bits
APPEND_CHAR ( uint32_t ( 0xf8 | ( ( c > > 24 ) & 0x03 ) ) ) ; // Top 2 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 18 ) & 0x3f ) ) ) ; // Upper middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 12 ) & 0x3f ) ) ) ; // middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 6 ) & 0x3f ) ) ) ; // Lower middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
} else if ( c < = 0x7fffffff ) { // 31 bits
APPEND_CHAR ( uint32_t ( 0xfc | ( ( c > > 30 ) & 0x01 ) ) ) ; // Top 1 bit.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 24 ) & 0x3f ) ) ) ; // Upper upper middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 18 ) & 0x3f ) ) ) ; // Lower upper middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 12 ) & 0x3f ) ) ) ; // Upper lower middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 6 ) & 0x3f ) ) ) ; // Lower lower middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
} else {
2023-03-11 10:31:41 +01:00
// the string is a valid UTF32, so it should never happen ...
print_unicode_error ( vformat ( " Non scalar value (%x) " , c ) , true ) ;
APPEND_CHAR ( uint32_t ( 0xe0 | ( ( _replacement_char > > 12 ) & 0x0f ) ) ) ; // Top 4 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( _replacement_char > > 6 ) & 0x3f ) ) ) ; // Middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( _replacement_char & 0x3f ) ) ) ; // Bottom 6 bits.
2014-02-10 02:10:30 +01:00
}
}
# undef APPEND_CHAR
* cdst = 0 ; //trailing zero
return utf8s ;
}
2020-07-27 12:43:20 +02:00
String String : : utf16 ( const char16_t * p_utf16 , int p_len ) {
String ret ;
ret . parse_utf16 ( p_utf16 , p_len ) ;
return ret ;
}
2022-07-05 14:18:29 +02:00
Error String : : parse_utf16 ( const char16_t * p_utf16 , int p_len ) {
2020-07-27 12:43:20 +02:00
if ( ! p_utf16 ) {
2022-07-05 14:18:29 +02:00
return ERR_INVALID_DATA ;
2020-07-27 12:43:20 +02:00
}
String aux ;
int cstr_size = 0 ;
int str_size = 0 ;
/* HANDLE BOM (Byte Order Mark) */
bool byteswap = false ; // assume correct endianness if no BOM found
if ( p_len < 0 | | p_len > = 1 ) {
bool has_bom = false ;
if ( uint16_t ( p_utf16 [ 0 ] ) = = 0xfeff ) { // correct BOM, read as is
has_bom = true ;
byteswap = false ;
} else if ( uint16_t ( p_utf16 [ 0 ] ) = = 0xfffe ) { // backwards BOM, swap bytes
has_bom = true ;
byteswap = true ;
}
if ( has_bom ) {
if ( p_len > = 0 ) {
p_len - = 1 ;
}
p_utf16 + = 1 ;
}
}
2022-07-05 14:18:29 +02:00
bool decode_error = false ;
2020-07-27 12:43:20 +02:00
{
const char16_t * ptrtmp = p_utf16 ;
const char16_t * ptrtmp_limit = & p_utf16 [ p_len ] ;
2022-07-05 14:18:29 +02:00
uint32_t c_prev = 0 ;
bool skip = false ;
2020-07-27 12:43:20 +02:00
while ( ptrtmp ! = ptrtmp_limit & & * ptrtmp ) {
uint32_t c = ( byteswap ) ? BSWAP16 ( * ptrtmp ) : * ptrtmp ;
2022-07-05 14:18:29 +02:00
if ( ( c & 0xfffffc00 ) = = 0xd800 ) { // lead surrogate
if ( skip ) {
print_unicode_error ( vformat ( " Unpaired lead surrogate (%x [trail?] %x) " , c_prev , c ) ) ;
decode_error = true ;
2020-07-27 12:43:20 +02:00
}
2022-07-05 14:18:29 +02:00
skip = true ;
} else if ( ( c & 0xfffffc00 ) = = 0xdc00 ) { // trail surrogate
if ( skip ) {
str_size - - ;
2020-07-27 12:43:20 +02:00
} else {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Unpaired trail surrogate (%x [lead?] %x) " , c_prev , c ) ) ;
decode_error = true ;
2020-07-27 12:43:20 +02:00
}
2022-07-05 14:18:29 +02:00
skip = false ;
} else {
skip = false ;
2020-07-27 12:43:20 +02:00
}
2022-07-05 14:18:29 +02:00
c_prev = c ;
str_size + + ;
2020-07-27 12:43:20 +02:00
cstr_size + + ;
ptrtmp + + ;
}
if ( skip ) {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Unpaired lead surrogate (%x [eol]) " , c_prev ) ) ;
decode_error = true ;
2020-07-27 12:43:20 +02:00
}
}
if ( str_size = = 0 ) {
clear ( ) ;
2022-07-05 14:18:29 +02:00
return OK ; // empty string
2020-07-27 12:43:20 +02:00
}
resize ( str_size + 1 ) ;
char32_t * dst = ptrw ( ) ;
dst [ str_size ] = 0 ;
2022-07-05 14:18:29 +02:00
bool skip = false ;
uint32_t c_prev = 0 ;
2020-07-27 12:43:20 +02:00
while ( cstr_size ) {
uint32_t c = ( byteswap ) ? BSWAP16 ( * p_utf16 ) : * p_utf16 ;
2022-07-05 14:18:29 +02:00
if ( ( c & 0xfffffc00 ) = = 0xd800 ) { // lead surrogate
if ( skip ) {
* ( dst + + ) = c_prev ; // unpaired, store as is
}
skip = true ;
} else if ( ( c & 0xfffffc00 ) = = 0xdc00 ) { // trail surrogate
if ( skip ) {
* ( dst + + ) = ( c_prev < < 10UL ) + c - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ; // decode pair
} else {
* ( dst + + ) = c ; // unpaired, store as is
}
skip = false ;
2020-07-27 12:43:20 +02:00
} else {
2022-07-05 14:18:29 +02:00
* ( dst + + ) = c ;
skip = false ;
2020-07-27 12:43:20 +02:00
}
2022-07-05 14:18:29 +02:00
cstr_size - - ;
p_utf16 + + ;
c_prev = c ;
}
2020-07-27 12:43:20 +02:00
2022-07-05 14:18:29 +02:00
if ( skip ) {
* ( dst + + ) = c_prev ;
2020-07-27 12:43:20 +02:00
}
2014-02-10 02:10:30 +01:00
2022-07-05 14:18:29 +02:00
if ( decode_error ) {
return ERR_PARSE_ERROR ;
} else {
return OK ;
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2020-07-27 12:43:20 +02:00
Char16String String : : utf16 ( ) const {
int l = length ( ) ;
if ( ! l ) {
return Char16String ( ) ;
}
const char32_t * d = & operator [ ] ( 0 ) ;
int fl = 0 ;
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
if ( c < = 0xffff ) { // 16 bits.
fl + = 1 ;
2022-07-05 14:18:29 +02:00
if ( ( c & 0xfffff800 ) = = 0xd800 ) {
print_unicode_error ( vformat ( " Unpaired surrogate (%x) " , c ) ) ;
}
2020-07-27 12:43:20 +02:00
} else if ( c < = 0x10ffff ) { // 32 bits.
fl + = 2 ;
} else {
2022-07-05 14:18:29 +02:00
print_unicode_error ( vformat ( " Invalid unicode codepoint (%x), cannot represent as UTF-16 " , c ) , true ) ;
fl + = 1 ;
2020-07-27 12:43:20 +02:00
}
}
Char16String utf16s ;
if ( fl = = 0 ) {
return utf16s ;
}
utf16s . resize ( fl + 1 ) ;
uint16_t * cdst = ( uint16_t * ) utf16s . get_data ( ) ;
# define APPEND_CHAR(m_c) *(cdst++) = m_c
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
if ( c < = 0xffff ) { // 16 bits.
APPEND_CHAR ( c ) ;
2022-07-05 14:18:29 +02:00
} else if ( c < = 0x10ffff ) { // 32 bits.
2020-07-27 12:43:20 +02:00
APPEND_CHAR ( uint32_t ( ( c > > 10 ) + 0xd7c0 ) ) ; // lead surrogate.
APPEND_CHAR ( uint32_t ( ( c & 0x3ff ) | 0xdc00 ) ) ; // trail surrogate.
2022-07-05 14:18:29 +02:00
} else {
2023-03-11 10:31:41 +01:00
// the string is a valid UTF32, so it should never happen ...
APPEND_CHAR ( uint32_t ( ( _replacement_char > > 10 ) + 0xd7c0 ) ) ;
APPEND_CHAR ( uint32_t ( ( _replacement_char & 0x3ff ) | 0xdc00 ) ) ;
2020-07-27 12:43:20 +02:00
}
}
# undef APPEND_CHAR
* cdst = 0 ; //trailing zero
2020-05-19 15:46:49 +02:00
2020-07-27 12:43:20 +02:00
return utf16s ;
}
2014-02-10 02:10:30 +01:00
String : : String ( const char * p_str ) {
copy_from ( p_str ) ;
}
2018-07-25 03:11:03 +02:00
2020-07-27 12:43:20 +02:00
String : : String ( const wchar_t * p_str ) {
copy_from ( p_str ) ;
}
String : : String ( const char32_t * p_str ) {
copy_from ( p_str ) ;
}
String : : String ( const char * p_str , int p_clip_to_len ) {
copy_from ( p_str , p_clip_to_len ) ;
}
String : : String ( const wchar_t * p_str , int p_clip_to_len ) {
copy_from ( p_str , p_clip_to_len ) ;
}
String : : String ( const char32_t * p_str , int p_clip_to_len ) {
2014-02-10 02:10:30 +01:00
copy_from ( p_str , p_clip_to_len ) ;
}
String : : String ( const StrRange & p_range ) {
2020-05-14 16:41:43 +02:00
if ( ! p_range . c_str ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
copy_from ( p_range . c_str , p_range . len ) ;
}
2021-01-28 13:39:05 +01:00
int64_t String : : hex_to_int ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
2017-01-09 00:58:39 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * s = ptr ( ) ;
2014-02-10 02:10:30 +01:00
2017-01-09 00:58:39 +01:00
int64_t sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
2014-02-10 02:10:30 +01:00
2017-01-09 00:58:39 +01:00
if ( sign < 0 ) {
s + + ;
}
2021-10-21 18:37:26 +02:00
if ( len > 2 & & s [ 0 ] = = ' 0 ' & & lower_case ( s [ 1 ] ) = = ' x ' ) {
2017-01-09 00:58:39 +01:00
s + = 2 ;
2018-04-18 22:20:39 +02:00
}
2017-01-09 00:58:39 +01:00
int64_t hex = 0 ;
while ( * s ) {
2021-08-12 19:48:42 +02:00
char32_t c = lower_case ( * s ) ;
2017-01-09 00:58:39 +01:00
int64_t n ;
2021-08-12 19:48:42 +02:00
if ( is_digit ( c ) ) {
2017-01-09 00:58:39 +01:00
n = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
n = ( c - ' a ' ) + 10 ;
} else {
2023-04-18 09:20:48 +02:00
ERR_FAIL_V_MSG ( 0 , vformat ( R " (Invalid hexadecimal notation character " % c " (U+%04X) in string " % s " .) " , * s , static_cast < int32_t > ( * s ) , * this ) ) ;
2017-01-09 00:58:39 +01:00
}
2020-07-01 09:13:12 +02:00
// Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
bool overflow = ( ( hex > INT64_MAX / 16 ) & & ( sign = = 1 | | ( sign = = - 1 & & hex ! = ( INT64_MAX > > 4 ) + 1 ) ) ) | | ( sign = = - 1 & & hex = = ( INT64_MAX > > 4 ) + 1 & & c > ' 0 ' ) ;
2022-05-20 14:12:09 +02:00
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + * this + " as a 64-bit signed integer, since the value is " + ( sign = = 1 ? " too large. " : " too small. " ) ) ;
2017-01-09 00:58:39 +01:00
hex * = 16 ;
hex + = n ;
s + + ;
}
2014-02-10 02:10:30 +01:00
2017-01-09 00:58:39 +01:00
return hex * sign ;
2014-02-10 02:10:30 +01:00
}
2021-01-28 13:39:05 +01:00
int64_t String : : bin_to_int ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
2019-04-25 14:43:48 +02:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2019-04-25 14:43:48 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * s = ptr ( ) ;
2019-04-25 14:43:48 +02:00
int64_t sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
if ( sign < 0 ) {
s + + ;
}
2021-10-21 18:48:23 +02:00
if ( len > 2 & & s [ 0 ] = = ' 0 ' & & lower_case ( s [ 1 ] ) = = ' b ' ) {
2019-04-25 14:43:48 +02:00
s + = 2 ;
}
int64_t binary = 0 ;
while ( * s ) {
2021-08-12 19:48:42 +02:00
char32_t c = lower_case ( * s ) ;
2019-04-25 14:43:48 +02:00
int64_t n ;
if ( c = = ' 0 ' | | c = = ' 1 ' ) {
n = c - ' 0 ' ;
} else {
return 0 ;
}
2020-07-01 09:13:12 +02:00
// Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
bool overflow = ( ( binary > INT64_MAX / 2 ) & & ( sign = = 1 | | ( sign = = - 1 & & binary ! = ( INT64_MAX > > 1 ) + 1 ) ) ) | | ( sign = = - 1 & & binary = = ( INT64_MAX > > 1 ) + 1 & & c > ' 0 ' ) ;
2022-05-20 14:12:09 +02:00
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + * this + " as a 64-bit signed integer, since the value is " + ( sign = = 1 ? " too large. " : " too small. " ) ) ;
2019-04-25 14:43:48 +02:00
binary * = 2 ;
binary + = n ;
s + + ;
}
return binary * sign ;
}
2020-05-13 11:31:51 +02:00
int64_t String : : to_int ( ) const {
2020-05-14 16:41:43 +02:00
if ( length ( ) = = 0 ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int to = ( find ( " . " ) > = 0 ) ? find ( " . " ) : length ( ) ;
int64_t integer = 0 ;
int64_t sign = 1 ;
for ( int i = 0 ; i < to ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2021-08-12 19:48:42 +02:00
if ( is_digit ( c ) ) {
2020-07-01 09:13:12 +02:00
bool overflow = ( integer > INT64_MAX / 10 ) | | ( integer = = INT64_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
2022-05-20 14:12:09 +02:00
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + * this + " as a 64-bit signed integer, since the value is " + ( sign = = 1 ? " too large. " : " too small. " ) ) ;
2014-02-10 02:10:30 +01:00
integer * = 10 ;
integer + = c - ' 0 ' ;
} else if ( integer = = 0 & & c = = ' - ' ) {
sign = - sign ;
}
}
return integer * sign ;
}
2020-06-03 06:04:04 +02:00
int64_t String : : to_int ( const char * p_str , int p_len ) {
2014-02-10 02:10:30 +01:00
int to = 0 ;
2020-05-14 16:41:43 +02:00
if ( p_len > = 0 ) {
2016-01-03 21:14:28 +01:00
to = p_len ;
2020-05-14 16:41:43 +02:00
} else {
while ( p_str [ to ] ! = 0 & & p_str [ to ] ! = ' . ' ) {
2016-01-03 21:14:28 +01:00
to + + ;
2020-05-14 16:41:43 +02:00
}
2016-01-03 21:14:28 +01:00
}
2014-02-10 02:10:30 +01:00
2020-06-03 06:04:04 +02:00
int64_t integer = 0 ;
int64_t sign = 1 ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < to ; i + + ) {
char c = p_str [ i ] ;
2021-08-12 19:48:42 +02:00
if ( is_digit ( c ) ) {
2020-07-01 09:13:12 +02:00
bool overflow = ( integer > INT64_MAX / 10 ) | | ( integer = = INT64_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
2022-05-20 14:12:09 +02:00
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + String ( p_str ) . substr ( 0 , to ) + " as a 64-bit signed integer, since the value is " + ( sign = = 1 ? " too large. " : " too small. " ) ) ;
2014-02-10 02:10:30 +01:00
integer * = 10 ;
integer + = c - ' 0 ' ;
} else if ( c = = ' - ' & & integer = = 0 ) {
sign = - sign ;
2020-05-14 16:41:43 +02:00
} else if ( c ! = ' ' ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return integer * sign ;
}
2020-07-27 12:43:20 +02:00
int64_t String : : to_int ( const wchar_t * p_str , int p_len ) {
int to = 0 ;
if ( p_len > = 0 ) {
to = p_len ;
} else {
while ( p_str [ to ] ! = 0 & & p_str [ to ] ! = ' . ' ) {
to + + ;
}
}
int64_t integer = 0 ;
int64_t sign = 1 ;
for ( int i = 0 ; i < to ; i + + ) {
wchar_t c = p_str [ i ] ;
2021-08-12 19:48:42 +02:00
if ( is_digit ( c ) ) {
2020-07-27 12:43:20 +02:00
bool overflow = ( integer > INT64_MAX / 10 ) | | ( integer = = INT64_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
2022-05-20 14:12:09 +02:00
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + String ( p_str ) . substr ( 0 , to ) + " as a 64-bit signed integer, since the value is " + ( sign = = 1 ? " too large. " : " too small. " ) ) ;
2020-07-27 12:43:20 +02:00
integer * = 10 ;
integer + = c - ' 0 ' ;
} else if ( c = = ' - ' & & integer = = 0 ) {
sign = - sign ;
} else if ( c ! = ' ' ) {
break ;
}
}
return integer * sign ;
}
2014-02-10 02:10:30 +01:00
bool String : : is_numeric ( ) const {
if ( length ( ) = = 0 ) {
return false ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
int s = 0 ;
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( 0 ) = = ' - ' ) {
2014-02-10 02:10:30 +01:00
+ + s ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
bool dot = false ;
for ( int i = s ; i < length ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2014-02-10 02:10:30 +01:00
if ( c = = ' . ' ) {
if ( dot ) {
return false ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
dot = true ;
2022-02-04 09:32:20 +01:00
} else if ( ! is_digit ( c ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-19 15:46:49 +02:00
}
}
2014-02-10 02:10:30 +01:00
return true ; // TODO: Use the parser below for this instead
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
template < class C >
2021-10-28 15:43:36 +02:00
static double built_in_strtod (
/* A decimal ASCII floating-point number,
* optionally preceded by white space . Must
* have form " -I.FE-X " , where I is the integer
* part of the mantissa , F is the fractional
* part of the mantissa , and X is the
* exponent . Either of the signs may be " + " ,
* " - " , or omitted . Either I or F may be
* omitted , or both . The decimal point isn ' t
* necessary unless F is present . The " E " may
* actually be an " e " . E and X may both be
* omitted ( but not just one ) . */
const C * string ,
/* If non-nullptr, store terminating Cacter's
* address here . */
C * * endPtr = nullptr ) {
/* Largest possible base 10 exponent. Any
* exponent larger than this will already
* produce underflow or overflow , so there ' s
* no need to worry about additional digits . */
static const int maxExponent = 511 ;
/* Table giving binary powers of 10. Entry
* is 10 ^ 2 ^ i . Used to convert decimal
* exponents into floating - point numbers . */
static const double powersOf10 [ ] = {
10. ,
100. ,
2014-02-10 02:10:30 +01:00
1.0e4 ,
1.0e8 ,
1.0e16 ,
1.0e32 ,
1.0e64 ,
1.0e128 ,
1.0e256
} ;
2019-12-10 05:13:02 +01:00
bool sign , expSign = false ;
2014-02-10 02:10:30 +01:00
double fraction , dblExp ;
const double * d ;
2018-10-03 14:05:17 +02:00
const C * p ;
int c ;
2021-10-28 15:43:36 +02:00
/* Exponent read from "EX" field. */
int exp = 0 ;
/* Exponent that derives from the fractional
* part . Under normal circumstances , it is
* the negative of the number of digits in F .
* However , if I is very long , the last digits
* of I get dropped ( otherwise a long I with a
* large negative exponent could cause an
* unnecessary overflow on I alone ) . In this
* case , fracExp is incremented one for each
* dropped digit . */
int fracExp = 0 ;
/* Number of digits in mantissa. */
int mantSize ;
/* Number of mantissa digits BEFORE decimal point. */
int decPt ;
/* Temporarily holds location of exponent in string. */
const C * pExp ;
2014-02-10 02:10:30 +01:00
/*
2021-10-28 15:43:36 +02:00
* Strip off leading blanks and check for a sign .
*/
2014-02-10 02:10:30 +01:00
p = string ;
while ( * p = = ' ' | | * p = = ' \t ' | | * p = = ' \n ' ) {
p + = 1 ;
}
if ( * p = = ' - ' ) {
sign = true ;
p + = 1 ;
} else {
if ( * p = = ' + ' ) {
p + = 1 ;
}
sign = false ;
}
2017-03-05 16:44:50 +01:00
2014-02-10 02:10:30 +01:00
/*
2021-10-28 15:43:36 +02:00
* Count the number of digits in the mantissa ( including the decimal
* point ) , and also locate the decimal point .
*/
2014-02-10 02:10:30 +01:00
decPt = - 1 ;
for ( mantSize = 0 ; ; mantSize + = 1 ) {
c = * p ;
2021-08-12 19:48:42 +02:00
if ( ! is_digit ( c ) ) {
2014-02-10 02:10:30 +01:00
if ( ( c ! = ' . ' ) | | ( decPt > = 0 ) ) {
break ;
}
decPt = mantSize ;
2017-03-05 16:44:50 +01:00
}
p + = 1 ;
2014-02-10 02:10:30 +01:00
}
/*
2021-10-28 15:43:36 +02:00
* Now suck up the digits in the mantissa . Use two integers to collect 9
* digits each ( this is faster than using floating - point ) . If the mantissa
* has more than 18 digits , ignore the extras , since they can ' t affect the
* value anyway .
*/
2014-02-10 02:10:30 +01:00
pExp = p ;
p - = mantSize ;
if ( decPt < 0 ) {
decPt = mantSize ;
} else {
mantSize - = 1 ; /* One of the digits was the point. */
}
if ( mantSize > 18 ) {
fracExp = decPt - 18 ;
mantSize = 18 ;
} else {
fracExp = decPt - mantSize ;
}
if ( mantSize = = 0 ) {
fraction = 0.0 ;
p = string ;
goto done ;
} else {
int frac1 , frac2 ;
2017-03-05 16:44:50 +01:00
2014-02-10 02:10:30 +01:00
frac1 = 0 ;
for ( ; mantSize > 9 ; mantSize - = 1 ) {
c = * p ;
p + = 1 ;
if ( c = = ' . ' ) {
c = * p ;
p + = 1 ;
}
frac1 = 10 * frac1 + ( c - ' 0 ' ) ;
}
frac2 = 0 ;
for ( ; mantSize > 0 ; mantSize - = 1 ) {
c = * p ;
p + = 1 ;
if ( c = = ' . ' ) {
c = * p ;
p + = 1 ;
}
frac2 = 10 * frac2 + ( c - ' 0 ' ) ;
2017-03-05 16:44:50 +01:00
}
2014-02-10 02:10:30 +01:00
fraction = ( 1.0e9 * frac1 ) + frac2 ;
}
/*
2021-10-28 15:43:36 +02:00
* Skim off the exponent .
*/
2014-02-10 02:10:30 +01:00
p = pExp ;
if ( ( * p = = ' E ' ) | | ( * p = = ' e ' ) ) {
p + = 1 ;
if ( * p = = ' - ' ) {
expSign = true ;
2017-03-05 16:44:50 +01:00
p + = 1 ;
2014-02-10 02:10:30 +01:00
} else {
if ( * p = = ' + ' ) {
2017-03-05 16:44:50 +01:00
p + = 1 ;
}
2014-02-10 02:10:30 +01:00
expSign = false ;
}
2021-08-12 19:48:42 +02:00
if ( ! is_digit ( char32_t ( * p ) ) ) {
2014-02-10 02:10:30 +01:00
p = pExp ;
goto done ;
2017-03-05 16:44:50 +01:00
}
2021-08-12 19:48:42 +02:00
while ( is_digit ( char32_t ( * p ) ) ) {
2014-02-10 02:10:30 +01:00
exp = exp * 10 + ( * p - ' 0 ' ) ;
2017-03-05 16:44:50 +01:00
p + = 1 ;
}
2014-02-10 02:10:30 +01:00
}
if ( expSign ) {
exp = fracExp - exp ;
2017-03-05 16:44:50 +01:00
} else {
2014-02-10 02:10:30 +01:00
exp = fracExp + exp ;
}
/*
2021-10-28 15:43:36 +02:00
* Generate a floating - point number that represents the exponent . Do this
* by processing the exponent one bit at a time to combine many powers of
* 2 of 10. Then combine the exponent with the fraction .
*/
2014-02-10 02:10:30 +01:00
if ( exp < 0 ) {
expSign = true ;
exp = - exp ;
} else {
expSign = false ;
}
if ( exp > maxExponent ) {
exp = maxExponent ;
WARN_PRINT ( " Exponent too high " ) ;
}
dblExp = 1.0 ;
for ( d = powersOf10 ; exp ! = 0 ; exp > > = 1 , + + d ) {
if ( exp & 01 ) {
dblExp * = * d ;
2017-03-05 16:44:50 +01:00
}
}
2014-02-10 02:10:30 +01:00
if ( expSign ) {
fraction / = dblExp ;
2017-03-05 16:44:50 +01:00
} else {
2014-02-10 02:10:30 +01:00
fraction * = dblExp ;
}
done :
2020-04-02 01:20:12 +02:00
if ( endPtr ! = nullptr ) {
2014-02-10 02:10:30 +01:00
* endPtr = ( C * ) p ;
}
if ( sign ) {
return - fraction ;
}
return fraction ;
}
# define READING_SIGN 0
# define READING_INT 1
# define READING_DEC 2
# define READING_EXP 3
# define READING_DONE 4
2020-07-24 20:07:57 +02:00
double String : : to_float ( const char * p_str ) {
2014-02-10 02:10:30 +01:00
return built_in_strtod < char > ( p_str ) ;
}
2020-07-27 12:43:20 +02:00
double String : : to_float ( const char32_t * p_str , const char32_t * * r_end ) {
return built_in_strtod < char32_t > ( p_str , ( char32_t * * ) r_end ) ;
}
double String : : to_float ( const wchar_t * p_str , const wchar_t * * r_end ) {
return built_in_strtod < wchar_t > ( p_str , ( wchar_t * * ) r_end ) ;
2014-02-10 02:10:30 +01:00
}
2023-04-06 17:54:56 +02:00
uint32_t String : : num_characters ( int64_t p_int ) {
int r = 1 ;
if ( p_int < 0 ) {
r + = 1 ;
if ( p_int = = INT64_MIN ) {
p_int = INT64_MAX ;
} else {
p_int = - p_int ;
}
}
while ( p_int > = 10 ) {
p_int / = 10 ;
r + + ;
}
return r ;
}
2020-07-27 12:43:20 +02:00
int64_t String : : to_int ( const char32_t * p_str , int p_len , bool p_clamp ) {
2020-05-14 16:41:43 +02:00
if ( p_len = = 0 | | ! p_str [ 0 ] ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
///@todo make more exact so saving and loading does not lose precision
int64_t integer = 0 ;
int64_t sign = 1 ;
int reading = READING_SIGN ;
2020-07-27 12:43:20 +02:00
const char32_t * str = p_str ;
const char32_t * limit = & p_str [ p_len ] ;
2014-02-10 02:10:30 +01:00
while ( * str & & reading ! = READING_DONE & & str ! = limit ) {
2020-07-27 12:43:20 +02:00
char32_t c = * ( str + + ) ;
2014-02-10 02:10:30 +01:00
switch ( reading ) {
case READING_SIGN : {
2021-08-12 19:48:42 +02:00
if ( is_digit ( c ) ) {
2014-02-10 02:10:30 +01:00
reading = READING_INT ;
// let it fallthrough
} else if ( c = = ' - ' ) {
sign = - 1 ;
reading = READING_INT ;
break ;
} else if ( c = = ' + ' ) {
sign = 1 ;
reading = READING_INT ;
break ;
} else {
break ;
}
2020-02-22 20:47:50 +01:00
[[fallthrough]] ;
2014-02-10 02:10:30 +01:00
}
case READING_INT : {
2021-08-12 19:48:42 +02:00
if ( is_digit ( c ) ) {
2019-09-22 18:45:08 +02:00
if ( integer > INT64_MAX / 10 ) {
2019-08-22 17:22:35 +02:00
String number ( " " ) ;
str = p_str ;
2019-08-24 13:34:14 +02:00
while ( * str & & str ! = limit ) {
2019-08-22 17:22:35 +02:00
number + = * ( str + + ) ;
}
2020-05-01 14:34:23 +02:00
if ( p_clamp ) {
if ( sign = = 1 ) {
return INT64_MAX ;
} else {
return INT64_MIN ;
}
} else {
2022-05-20 14:12:09 +02:00
ERR_FAIL_V_MSG ( sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + number + " as a 64-bit signed integer, since the value is " + ( sign = = 1 ? " too large. " : " too small. " ) ) ;
2020-05-01 14:34:23 +02:00
}
2019-08-22 17:22:35 +02:00
}
2014-02-10 02:10:30 +01:00
integer * = 10 ;
integer + = c - ' 0 ' ;
} else {
reading = READING_DONE ;
}
} break ;
}
}
return sign * integer ;
}
2020-07-24 20:07:57 +02:00
double String : : to_float ( ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
return built_in_strtod < char32_t > ( get_data ( ) ) ;
2014-02-10 02:10:30 +01:00
}
uint32_t String : : hash ( const char * p_cstr ) {
2023-09-24 15:58:28 +02:00
// static_cast: avoid negative values on platforms where char is signed.
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
2023-09-24 15:58:28 +02:00
uint32_t c = static_cast < uint8_t > ( * p_cstr + + ) ;
2016-03-09 00:00:52 +01:00
2022-09-28 15:59:08 +02:00
while ( c ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2023-09-24 15:58:28 +02:00
c = static_cast < uint8_t > ( * p_cstr + + ) ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
return hashv ;
2014-02-10 02:10:30 +01:00
}
uint32_t String : : hash ( const char * p_cstr , int p_len ) {
uint32_t hashv = 5381 ;
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < p_len ; i + + ) {
2023-09-24 15:58:28 +02:00
// static_cast: avoid negative values on platforms where char is signed.
hashv = ( ( hashv < < 5 ) + hashv ) + static_cast < uint8_t > ( p_cstr [ i ] ) ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
2020-07-27 12:43:20 +02:00
uint32_t String : : hash ( const wchar_t * p_cstr , int p_len ) {
2023-09-24 15:58:28 +02:00
// Avoid negative values on platforms where wchar_t is signed. Account for different sizes.
using wide_unsigned = std : : conditional < sizeof ( wchar_t ) = = 2 , uint16_t , uint32_t > : : type ;
2020-07-27 12:43:20 +02:00
uint32_t hashv = 5381 ;
for ( int i = 0 ; i < p_len ; i + + ) {
2023-09-24 15:58:28 +02:00
hashv = ( ( hashv < < 5 ) + hashv ) + static_cast < wide_unsigned > ( p_cstr [ i ] ) ; /* hash * 33 + c */
2020-07-27 12:43:20 +02:00
}
return hashv ;
}
uint32_t String : : hash ( const wchar_t * p_cstr ) {
2023-09-24 15:58:28 +02:00
// Avoid negative values on platforms where wchar_t is signed. Account for different sizes.
using wide_unsigned = std : : conditional < sizeof ( wchar_t ) = = 2 , uint16_t , uint32_t > : : type ;
2020-07-27 12:43:20 +02:00
uint32_t hashv = 5381 ;
2023-09-24 15:58:28 +02:00
uint32_t c = static_cast < wide_unsigned > ( * p_cstr + + ) ;
2020-07-27 12:43:20 +02:00
2022-09-28 15:59:08 +02:00
while ( c ) {
2020-07-27 12:43:20 +02:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2023-09-24 15:58:28 +02:00
c = static_cast < wide_unsigned > ( * p_cstr + + ) ;
2020-07-27 12:43:20 +02:00
}
return hashv ;
}
uint32_t String : : hash ( const char32_t * p_cstr , int p_len ) {
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < p_len ; i + + ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + p_cstr [ i ] ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
2020-07-27 12:43:20 +02:00
uint32_t String : : hash ( const char32_t * p_cstr ) {
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
2022-09-28 15:59:08 +02:00
uint32_t c = * p_cstr + + ;
2014-02-10 02:10:30 +01:00
2022-09-28 15:59:08 +02:00
while ( c ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2022-09-28 15:59:08 +02:00
c = * p_cstr + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
uint32_t String : : hash ( ) const {
/* simple djb2 hashing */
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * chr = get_data ( ) ;
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
2022-09-28 15:59:08 +02:00
uint32_t c = * chr + + ;
2016-03-09 00:00:52 +01:00
2022-09-28 15:59:08 +02:00
while ( c ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2022-09-28 15:59:08 +02:00
c = * chr + + ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return hashv ;
}
uint64_t String : : hash64 ( ) const {
/* simple djb2 hashing */
2020-07-27 12:43:20 +02:00
const char32_t * chr = get_data ( ) ;
2014-02-10 02:10:30 +01:00
uint64_t hashv = 5381 ;
2022-09-28 15:59:08 +02:00
uint64_t c = * chr + + ;
2014-02-10 02:10:30 +01:00
2022-09-28 15:59:08 +02:00
while ( c ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2022-09-28 15:59:08 +02:00
c = * chr + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
2014-03-14 02:57:24 +01:00
String String : : md5_text ( ) const {
CharString cs = utf8 ( ) ;
2019-07-02 03:06:52 +02:00
unsigned char hash [ 16 ] ;
CryptoCore : : md5 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
return String : : hex_encode_buffer ( hash , 16 ) ;
2014-03-14 02:57:24 +01:00
}
2014-02-10 02:10:30 +01:00
2019-07-02 16:07:02 +02:00
String String : : sha1_text ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 20 ] ;
CryptoCore : : sha1 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
return String : : hex_encode_buffer ( hash , 20 ) ;
}
2016-06-17 09:55:16 +02:00
String String : : sha256_text ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 32 ] ;
2019-07-02 03:06:52 +02:00
CryptoCore : : sha256 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
2016-06-17 09:55:16 +02:00
return String : : hex_encode_buffer ( hash , 32 ) ;
}
2014-08-02 03:10:38 +02:00
Vector < uint8_t > String : : md5_buffer ( ) const {
CharString cs = utf8 ( ) ;
2019-07-02 03:06:52 +02:00
unsigned char hash [ 16 ] ;
CryptoCore : : md5 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
2014-08-02 03:10:38 +02:00
Vector < uint8_t > ret ;
ret . resize ( 16 ) ;
for ( int i = 0 ; i < 16 ; i + + ) {
2019-07-02 03:06:52 +02:00
ret . write [ i ] = hash [ i ] ;
}
2014-08-02 03:10:38 +02:00
return ret ;
2020-05-19 15:46:49 +02:00
}
2014-08-02 03:10:38 +02:00
2019-07-02 16:07:02 +02:00
Vector < uint8_t > String : : sha1_buffer ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 20 ] ;
CryptoCore : : sha1 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
Vector < uint8_t > ret ;
ret . resize ( 20 ) ;
for ( int i = 0 ; i < 20 ; i + + ) {
ret . write [ i ] = hash [ i ] ;
}
return ret ;
}
2016-06-23 18:57:45 +02:00
Vector < uint8_t > String : : sha256_buffer ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 32 ] ;
2019-07-02 03:06:52 +02:00
CryptoCore : : sha256 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
2016-06-23 18:57:45 +02:00
Vector < uint8_t > ret ;
ret . resize ( 32 ) ;
for ( int i = 0 ; i < 32 ; i + + ) {
2018-07-25 03:11:03 +02:00
ret . write [ i ] = hash [ i ] ;
2016-06-23 18:57:45 +02:00
}
return ret ;
}
2017-12-11 23:38:07 +01:00
String String : : insert ( int p_at_pos , const String & p_string ) const {
2020-05-14 16:41:43 +02:00
if ( p_at_pos < 0 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( p_at_pos > length ( ) ) {
2014-02-10 02:10:30 +01:00
p_at_pos = length ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String pre ;
2020-05-14 16:41:43 +02:00
if ( p_at_pos > 0 ) {
2014-02-10 02:10:30 +01:00
pre = substr ( 0 , p_at_pos ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String post ;
2020-05-14 16:41:43 +02:00
if ( p_at_pos < length ( ) ) {
2014-02-10 02:10:30 +01:00
post = substr ( p_at_pos , length ( ) - p_at_pos ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return pre + p_string + post ;
}
2020-05-14 14:29:06 +02:00
2023-03-30 15:45:45 +02:00
String String : : erase ( int p_pos , int p_chars ) const {
ERR_FAIL_COND_V_MSG ( p_pos < 0 , " " , vformat ( " Invalid starting position for `String.erase()`: %d. Starting position must be positive or zero. " , p_pos ) ) ;
ERR_FAIL_COND_V_MSG ( p_chars < 0 , " " , vformat ( " Invalid character count for `String.erase()`: %d. Character count must be positive or zero. " , p_chars ) ) ;
return left ( p_pos ) + substr ( p_pos + p_chars ) ;
}
2014-02-10 02:10:30 +01:00
String String : : substr ( int p_from , int p_chars ) const {
2020-05-14 16:41:43 +02:00
if ( p_chars = = - 1 ) {
2019-05-03 14:21:04 +02:00
p_chars = length ( ) - p_from ;
2020-05-14 16:41:43 +02:00
}
2019-05-03 14:21:04 +02:00
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) | | p_from < 0 | | p_from > = length ( ) | | p_chars < = 0 ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
if ( ( p_from + p_chars ) > length ( ) ) {
p_chars = length ( ) - p_from ;
}
2017-12-11 23:38:07 +01:00
if ( p_from = = 0 & & p_chars > = length ( ) ) {
return String ( * this ) ;
}
2022-11-14 18:21:06 +01:00
String s ;
2020-07-27 12:43:20 +02:00
s . copy_from_unchecked ( & get_data ( ) [ p_from ] , p_chars ) ;
2018-07-24 17:06:49 +02:00
return s ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
int String : : find ( const String & p_str , int p_from ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
const int src_len = p_str . length ( ) ;
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
const int len = length ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
const char32_t * str = p_str . get_data ( ) ;
2014-02-10 02:10:30 +01:00
for ( int i = p_from ; i < = ( len - src_len ) ; i + + ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
if ( src [ read_pos ] ! = str [ j ] ) {
2014-02-10 02:10:30 +01:00
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
2017-12-11 23:38:07 +01:00
int String : : find ( const char * p_str , int p_from ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2017-12-11 23:38:07 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
const int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2017-12-11 23:38:07 +01:00
int src_len = 0 ;
2020-05-14 16:41:43 +02:00
while ( p_str [ src_len ] ! = ' \0 ' ) {
2017-12-11 23:38:07 +01:00
src_len + + ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
if ( src_len = = 1 ) {
2020-07-27 12:43:20 +02:00
const char32_t needle = p_str [ 0 ] ;
2017-12-11 23:38:07 +01:00
for ( int i = p_from ; i < len ; i + + ) {
if ( src [ i ] = = needle ) {
return i ;
}
}
} else {
for ( int i = p_from ; i < = ( len - src_len ) ; i + + ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2017-12-11 23:38:07 +01:00
2020-07-27 12:43:20 +02:00
if ( src [ read_pos ] ! = ( char32_t ) p_str [ j ] ) {
2017-12-11 23:38:07 +01:00
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2017-12-11 23:38:07 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
}
}
return - 1 ;
}
2020-07-27 12:43:20 +02:00
int String : : find_char ( const char32_t & p_char , int p_from ) const {
2019-01-07 18:02:55 +01:00
return _cowdata . find ( p_char , p_from ) ;
}
2014-02-10 02:10:30 +01:00
int String : : findmk ( const Vector < String > & p_keys , int p_from , int * r_key ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
if ( p_keys . size ( ) = = 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
//int src_len=p_str.length();
const String * keys = & p_keys [ 0 ] ;
int key_count = p_keys . size ( ) ;
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2014-02-10 02:10:30 +01:00
2016-10-20 14:58:00 +02:00
for ( int i = p_from ; i < len ; i + + ) {
2014-02-10 02:10:30 +01:00
bool found = true ;
for ( int k = 0 ; k < key_count ; k + + ) {
found = true ;
2020-05-14 16:41:43 +02:00
if ( r_key ) {
2014-02-10 02:10:30 +01:00
* r_key = k ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
const char32_t * cmp = keys [ k ] . get_data ( ) ;
2014-02-10 02:10:30 +01:00
int l = keys [ k ] . length ( ) ;
for ( int j = 0 ; j < l ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
found = false ;
break ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
if ( src [ read_pos ] ! = cmp [ j ] ) {
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
2017-12-11 23:38:07 +01:00
int String : : findn ( const String & p_str , int p_from ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int src_len = p_str . length ( ) ;
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | length ( ) = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * srcd = get_data ( ) ;
2014-02-10 02:10:30 +01:00
for ( int i = p_from ; i < = ( length ( ) - src_len ) ; i + + ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = length ( ) ) {
ERR_PRINT ( " read_pos>=length() " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
char32_t src = _find_lower ( srcd [ read_pos ] ) ;
char32_t dst = _find_lower ( p_str [ j ] ) ;
2014-02-10 02:10:30 +01:00
if ( src ! = dst ) {
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
2017-12-11 23:38:07 +01:00
int String : : rfind ( const String & p_str , int p_from ) const {
2017-09-22 05:58:29 +02:00
// establish a limit
2014-09-21 06:43:42 +02:00
int limit = length ( ) - p_str . length ( ) ;
2020-05-14 16:41:43 +02:00
if ( limit < 0 ) {
2014-09-21 06:43:42 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2017-09-22 05:58:29 +02:00
// establish a starting point
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-09-21 06:43:42 +02:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
} else if ( p_from > limit ) {
2014-09-21 06:43:42 +02:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
int src_len = p_str . length ( ) ;
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | len = = 0 ) {
2017-09-22 05:58:29 +02:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2014-09-21 06:43:42 +02:00
for ( int i = p_from ; i > = 0 ; i - - ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-09-21 06:43:42 +02:00
if ( src [ read_pos ] ! = p_str [ j ] ) {
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-09-21 06:43:42 +02:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
}
2014-02-10 02:10:30 +01:00
return - 1 ;
}
2020-05-14 14:29:06 +02:00
2017-12-11 23:38:07 +01:00
int String : : rfindn ( const String & p_str , int p_from ) const {
2017-09-22 05:58:29 +02:00
// establish a limit
2014-09-21 06:43:42 +02:00
int limit = length ( ) - p_str . length ( ) ;
2020-05-14 16:41:43 +02:00
if ( limit < 0 ) {
2014-09-21 06:43:42 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2017-09-22 05:58:29 +02:00
// establish a starting point
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-09-21 06:43:42 +02:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
} else if ( p_from > limit ) {
2014-09-21 06:43:42 +02:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
int src_len = p_str . length ( ) ;
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2014-09-21 06:43:42 +02:00
for ( int i = p_from ; i > = 0 ; i - - ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-09-21 06:43:42 +02:00
2020-07-27 12:43:20 +02:00
char32_t srcc = _find_lower ( src [ read_pos ] ) ;
char32_t dstc = _find_lower ( p_str [ j ] ) ;
2014-09-21 06:43:42 +02:00
if ( srcc ! = dstc ) {
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-09-21 06:43:42 +02:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
}
2014-02-10 02:10:30 +01:00
return - 1 ;
}
bool String : : ends_with ( const String & p_string ) const {
2021-01-19 13:14:25 +01:00
int l = p_string . length ( ) ;
2021-02-05 20:12:25 +01:00
if ( l > length ( ) ) {
return false ;
}
2021-01-19 13:14:25 +01:00
if ( l = = 0 ) {
return true ;
}
2021-02-05 20:12:25 +01:00
const char32_t * p = & p_string [ 0 ] ;
const char32_t * s = & operator [ ] ( length ( ) - l ) ;
for ( int i = 0 ; i < l ; i + + ) {
if ( p [ i ] ! = s [ i ] ) {
return false ;
}
2020-05-14 16:41:43 +02:00
}
2021-02-05 20:12:25 +01:00
return true ;
2014-02-10 02:10:30 +01:00
}
bool String : : begins_with ( const String & p_string ) const {
2021-02-05 20:12:25 +01:00
int l = p_string . length ( ) ;
if ( l > length ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( l = = 0 ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2021-02-05 20:12:25 +01:00
const char32_t * p = & p_string [ 0 ] ;
const char32_t * s = & operator [ ] ( 0 ) ;
2016-03-09 00:00:52 +01:00
2021-02-05 20:12:25 +01:00
for ( int i = 0 ; i < l ; i + + ) {
if ( p [ i ] ! = s [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2021-02-05 20:12:25 +01:00
return true ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
bool String : : begins_with ( const char * p_string ) const {
int l = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( l = = 0 | | ! p_string ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * str = & operator [ ] ( 0 ) ;
2014-02-10 02:10:30 +01:00
int i = 0 ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( * p_string & & i < l ) {
2020-07-27 12:43:20 +02:00
if ( ( char32_t ) * p_string ! = str [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
i + + ;
p_string + + ;
}
2016-03-09 00:00:52 +01:00
2014-02-17 21:46:25 +01:00
return * p_string = = 0 ;
2014-02-10 02:10:30 +01:00
}
2017-10-28 23:46:20 +02:00
bool String : : is_enclosed_in ( const String & p_string ) const {
return begins_with ( p_string ) & & ends_with ( p_string ) ;
}
2016-06-12 05:25:01 +02:00
bool String : : is_subsequence_of ( const String & p_string ) const {
return _base_is_subsequence_of ( p_string , false ) ;
}
2022-01-27 00:03:56 +01:00
bool String : : is_subsequence_ofn ( const String & p_string ) const {
2016-06-12 05:25:01 +02:00
return _base_is_subsequence_of ( p_string , true ) ;
}
2017-10-28 23:46:20 +02:00
bool String : : is_quoted ( ) const {
return is_enclosed_in ( " \" " ) | | is_enclosed_in ( " ' " ) ;
}
2019-01-18 09:29:28 +01:00
int String : : _count ( const String & p_string , int p_from , int p_to , bool p_case_insensitive ) const {
2020-12-15 13:04:21 +01:00
if ( p_string . is_empty ( ) ) {
2019-01-18 09:29:28 +01:00
return 0 ;
}
int len = length ( ) ;
int slen = p_string . length ( ) ;
if ( len < slen ) {
return 0 ;
}
String str ;
if ( p_from > = 0 & & p_to > = 0 ) {
if ( p_to = = 0 ) {
p_to = len ;
} else if ( p_from > = p_to ) {
return 0 ;
}
if ( p_from = = 0 & & p_to = = len ) {
str = String ( ) ;
2020-07-27 12:43:20 +02:00
str . copy_from_unchecked ( & get_data ( ) [ 0 ] , len ) ;
2019-01-18 09:29:28 +01:00
} else {
str = substr ( p_from , p_to - p_from ) ;
}
} else {
return 0 ;
}
int c = 0 ;
int idx = - 1 ;
do {
idx = p_case_insensitive ? str . findn ( p_string ) : str . find ( p_string ) ;
if ( idx ! = - 1 ) {
str = str . substr ( idx + slen , str . length ( ) - slen ) ;
+ + c ;
}
} while ( idx ! = - 1 ) ;
return c ;
}
int String : : count ( const String & p_string , int p_from , int p_to ) const {
return _count ( p_string , p_from , p_to , false ) ;
}
int String : : countn ( const String & p_string , int p_from , int p_to ) const {
return _count ( p_string , p_from , p_to , true ) ;
}
2016-06-12 05:25:01 +02:00
bool String : : _base_is_subsequence_of ( const String & p_string , bool case_insensitive ) const {
int len = length ( ) ;
if ( len = = 0 ) {
// Technically an empty string is subsequence of any string
return true ;
}
if ( len > p_string . length ( ) ) {
return false ;
}
2020-07-27 12:43:20 +02:00
const char32_t * src = & operator [ ] ( 0 ) ;
const char32_t * tgt = & p_string [ 0 ] ;
2016-06-12 05:25:01 +02:00
for ( ; * src & & * tgt ; tgt + + ) {
bool match = false ;
if ( case_insensitive ) {
2020-07-27 12:43:20 +02:00
char32_t srcc = _find_lower ( * src ) ;
char32_t tgtc = _find_lower ( * tgt ) ;
2016-06-12 05:25:01 +02:00
match = srcc = = tgtc ;
} else {
match = * src = = * tgt ;
}
if ( match ) {
src + + ;
if ( ! * src ) {
return true ;
}
}
}
return false ;
}
2014-02-10 02:10:30 +01:00
2016-06-13 19:06:03 +02:00
Vector < String > String : : bigrams ( ) const {
int n_pairs = length ( ) - 1 ;
Vector < String > b ;
if ( n_pairs < = 0 ) {
return b ;
}
b . resize ( n_pairs ) ;
for ( int i = 0 ; i < n_pairs ; i + + ) {
2018-07-25 03:11:03 +02:00
b . write [ i ] = substr ( i , 2 ) ;
2016-06-13 19:06:03 +02:00
}
return b ;
}
// Similarity according to Sorensen-Dice coefficient
float String : : similarity ( const String & p_string ) const {
if ( operator = = ( p_string ) ) {
// Equal strings are totally similar
return 1.0f ;
}
if ( length ( ) < 2 | | p_string . length ( ) < 2 ) {
// No way to calculate similarity without a single bigram
return 0.0f ;
}
Vector < String > src_bigrams = bigrams ( ) ;
Vector < String > tgt_bigrams = p_string . bigrams ( ) ;
int src_size = src_bigrams . size ( ) ;
int tgt_size = tgt_bigrams . size ( ) ;
2021-01-25 20:46:35 +01:00
int sum = src_size + tgt_size ;
int inter = 0 ;
2016-06-13 19:06:03 +02:00
for ( int i = 0 ; i < src_size ; i + + ) {
for ( int j = 0 ; j < tgt_size ; j + + ) {
if ( src_bigrams [ i ] = = tgt_bigrams [ j ] ) {
inter + + ;
break ;
}
}
}
return ( 2.0f * inter ) / sum ;
}
2020-07-27 12:43:20 +02:00
static bool _wildcard_match ( const char32_t * p_pattern , const char32_t * p_string , bool p_case_sensitive ) {
2014-02-10 02:10:30 +01:00
switch ( * p_pattern ) {
case ' \0 ' :
return ! * p_string ;
case ' * ' :
return _wildcard_match ( p_pattern + 1 , p_string , p_case_sensitive ) | | ( * p_string & & _wildcard_match ( p_pattern , p_string + 1 , p_case_sensitive ) ) ;
case ' ? ' :
return * p_string & & ( * p_string ! = ' . ' ) & & _wildcard_match ( p_pattern + 1 , p_string + 1 , p_case_sensitive ) ;
default :
2017-03-05 16:44:50 +01:00
2014-02-10 02:10:30 +01:00
return ( p_case_sensitive ? ( * p_string = = * p_pattern ) : ( _find_upper ( * p_string ) = = _find_upper ( * p_pattern ) ) ) & & _wildcard_match ( p_pattern + 1 , p_string + 1 , p_case_sensitive ) ;
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
}
bool String : : match ( const String & p_wildcard ) const {
2020-05-14 16:41:43 +02:00
if ( ! p_wildcard . length ( ) | | ! length ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
return _wildcard_match ( p_wildcard . get_data ( ) , get_data ( ) , true ) ;
2014-02-10 02:10:30 +01:00
}
bool String : : matchn ( const String & p_wildcard ) const {
2020-05-14 16:41:43 +02:00
if ( ! p_wildcard . length ( ) | | ! length ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
return _wildcard_match ( p_wildcard . get_data ( ) , get_data ( ) , false ) ;
2014-02-10 02:10:30 +01:00
}
2023-11-02 18:36:57 +01:00
String String : : format ( const Variant & values , const String & placeholder ) const {
2024-01-28 21:51:39 +01:00
String new_string = String ( ptr ( ) ) ;
2016-11-01 15:14:48 +01:00
if ( values . get_type ( ) = = Variant : : ARRAY ) {
Array values_arr = values ;
for ( int i = 0 ; i < values_arr . size ( ) ; i + + ) {
String i_as_str = String : : num_int64 ( i ) ;
if ( values_arr [ i ] . get_type ( ) = = Variant : : ARRAY ) { //Array in Array structure [["name","RobotGuy"],[0,"godot"],["strength",9000.91]]
Array value_arr = values_arr [ i ] ;
if ( value_arr . size ( ) = = 2 ) {
Variant v_key = value_arr [ 0 ] ;
2018-10-19 21:53:37 +02:00
String key = v_key ;
2016-11-01 15:14:48 +01:00
Variant v_val = value_arr [ 1 ] ;
2018-10-19 21:53:37 +02:00
String val = v_val ;
2016-11-01 15:14:48 +01:00
2018-07-25 12:14:48 +02:00
new_string = new_string . replace ( placeholder . replace ( " _ " , key ) , val ) ;
2016-11-01 15:14:48 +01:00
} else {
ERR_PRINT ( String ( " STRING.format Inner Array size != 2 " ) . ascii ( ) . get_data ( ) ) ;
}
} else { //Array structure ["RobotGuy","Logis","rookie"]
Variant v_val = values_arr [ i ] ;
2018-10-19 21:53:37 +02:00
String val = v_val ;
2016-11-01 15:14:48 +01:00
2018-07-25 13:22:20 +02:00
if ( placeholder . find ( " _ " ) > - 1 ) {
new_string = new_string . replace ( placeholder . replace ( " _ " , i_as_str ) , val ) ;
} else {
new_string = new_string . replace_first ( placeholder , val ) ;
}
2016-11-01 15:14:48 +01:00
}
}
} else if ( values . get_type ( ) = = Variant : : DICTIONARY ) {
Dictionary d = values ;
List < Variant > keys ;
d . get_key_list ( & keys ) ;
2021-07-24 15:46:25 +02:00
for ( const Variant & key : keys ) {
2021-07-16 05:45:57 +02:00
new_string = new_string . replace ( placeholder . replace ( " _ " , key ) , d [ key ] ) ;
2016-11-01 15:14:48 +01:00
}
} else {
ERR_PRINT ( String ( " Invalid type: use Array or Dictionary. " ) . ascii ( ) . get_data ( ) ) ;
}
return new_string ;
}
2017-12-11 23:38:07 +01:00
String String : : replace ( const String & p_key , const String & p_with ) const {
2014-02-10 02:10:30 +01:00
String new_string ;
int search_from = 0 ;
int result = 0 ;
while ( ( result = find ( p_key , search_from ) ) > = 0 ) {
new_string + = substr ( search_from , result - search_from ) ;
new_string + = p_with ;
search_from = result + p_key . length ( ) ;
}
2017-12-11 23:38:07 +01:00
if ( search_from = = 0 ) {
return * this ;
}
2014-02-10 02:10:30 +01:00
new_string + = substr ( search_from , length ( ) - search_from ) ;
return new_string ;
}
2017-12-11 23:38:07 +01:00
String String : : replace ( const char * p_key , const char * p_with ) const {
String new_string ;
int search_from = 0 ;
int result = 0 ;
while ( ( result = find ( p_key , search_from ) ) > = 0 ) {
new_string + = substr ( search_from , result - search_from ) ;
new_string + = p_with ;
int k = 0 ;
2020-05-14 16:41:43 +02:00
while ( p_key [ k ] ! = ' \0 ' ) {
2017-12-11 23:38:07 +01:00
k + + ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
search_from = result + k ;
}
if ( search_from = = 0 ) {
return * this ;
}
new_string + = substr ( search_from , length ( ) - search_from ) ;
return new_string ;
}
String String : : replace_first ( const String & p_key , const String & p_with ) const {
2019-06-03 21:52:50 +02:00
int pos = find ( p_key ) ;
if ( pos > = 0 ) {
return substr ( 0 , pos ) + p_with + substr ( pos + p_key . length ( ) , length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2019-06-03 21:52:50 +02:00
return * this ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2017-12-11 23:38:07 +01:00
String String : : replacen ( const String & p_key , const String & p_with ) const {
2014-02-10 02:10:30 +01:00
String new_string ;
int search_from = 0 ;
int result = 0 ;
while ( ( result = findn ( p_key , search_from ) ) > = 0 ) {
new_string + = substr ( search_from , result - search_from ) ;
new_string + = p_with ;
search_from = result + p_key . length ( ) ;
}
2017-12-11 23:38:07 +01:00
if ( search_from = = 0 ) {
return * this ;
}
2014-02-10 02:10:30 +01:00
new_string + = substr ( search_from , length ( ) - search_from ) ;
return new_string ;
}
2019-09-02 05:49:55 +02:00
String String : : repeat ( int p_count ) const {
ERR_FAIL_COND_V_MSG ( p_count < 0 , " " , " Parameter count should be a positive number. " ) ;
2023-01-29 00:52:05 +01:00
if ( p_count = = 0 ) {
return " " ;
}
if ( p_count = = 1 ) {
return * this ;
}
2022-08-16 11:01:36 +02:00
int len = length ( ) ;
String new_string = * this ;
new_string . resize ( p_count * len + 1 ) ;
char32_t * dst = new_string . ptrw ( ) ;
int offset = 1 ;
int stride = 1 ;
while ( offset < p_count ) {
memcpy ( dst + offset * len , dst , stride * len * sizeof ( char32_t ) ) ;
offset + = stride ;
stride = MIN ( stride * 2 , p_count - offset ) ;
}
dst [ p_count * len ] = _null ;
2019-09-02 05:49:55 +02:00
return new_string ;
}
2023-06-21 19:40:48 +02:00
String String : : reverse ( ) const {
int len = length ( ) ;
if ( len < = 1 ) {
return * this ;
}
String new_string ;
new_string . resize ( len + 1 ) ;
const char32_t * src = ptr ( ) ;
char32_t * dst = new_string . ptrw ( ) ;
for ( int i = 0 ; i < len ; i + + ) {
dst [ i ] = src [ len - i - 1 ] ;
}
dst [ len ] = _null ;
return new_string ;
}
2022-06-12 04:45:58 +02:00
String String : : left ( int p_len ) const {
if ( p_len < 0 ) {
p_len = length ( ) + p_len ;
2020-02-13 16:42:49 +01:00
}
2022-06-12 04:45:58 +02:00
if ( p_len < = 0 ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-06-12 04:45:58 +02:00
if ( p_len > = length ( ) ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-08-20 19:01:06 +02:00
String s ;
s . copy_from_unchecked ( & get_data ( ) [ 0 ] , p_len ) ;
return s ;
2014-02-10 02:10:30 +01:00
}
2022-06-12 04:45:58 +02:00
String String : : right ( int p_len ) const {
if ( p_len < 0 ) {
p_len = length ( ) + p_len ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-06-12 04:45:58 +02:00
if ( p_len < = 0 ) {
2020-02-13 16:42:49 +01:00
return " " ;
}
2022-06-12 04:45:58 +02:00
if ( p_len > = length ( ) ) {
2019-02-20 16:36:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2019-02-20 16:36:30 +01:00
2023-08-20 19:01:06 +02:00
String s ;
s . copy_from_unchecked ( & get_data ( ) [ length ( ) - p_len ] , p_len ) ;
return s ;
2014-02-10 02:10:30 +01:00
}
2020-11-23 12:47:11 +01:00
char32_t String : : unicode_at ( int p_idx ) const {
2014-02-10 02:10:30 +01:00
ERR_FAIL_INDEX_V ( p_idx , length ( ) , 0 ) ;
return operator [ ] ( p_idx ) ;
}
2021-12-15 16:01:06 +01:00
String String : : indent ( const String & p_prefix ) const {
String new_string ;
int line_start = 0 ;
for ( int i = 0 ; i < length ( ) ; i + + ) {
const char32_t c = operator [ ] ( i ) ;
if ( c = = ' \n ' ) {
if ( i = = line_start ) {
new_string + = c ; // Leave empty lines empty.
} else {
new_string + = p_prefix + substr ( line_start , i - line_start + 1 ) ;
}
line_start = i + 1 ;
}
}
if ( line_start ! = length ( ) ) {
new_string + = p_prefix + substr ( line_start ) ;
}
return new_string ;
}
2017-10-11 10:27:54 +02:00
String String : : dedent ( ) const {
String new_string ;
String indent ;
bool has_indent = false ;
bool has_text = false ;
int line_start = 0 ;
int indent_stop = - 1 ;
for ( int i = 0 ; i < length ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2017-10-11 10:27:54 +02:00
if ( c = = ' \n ' ) {
2020-05-14 16:41:43 +02:00
if ( has_text ) {
2017-10-11 10:27:54 +02:00
new_string + = substr ( indent_stop , i - indent_stop ) ;
2020-05-14 16:41:43 +02:00
}
2017-10-11 10:27:54 +02:00
new_string + = " \n " ;
has_text = false ;
line_start = i + 1 ;
indent_stop = - 1 ;
} else if ( ! has_text ) {
if ( c > 32 ) {
has_text = true ;
if ( ! has_indent ) {
has_indent = true ;
indent = substr ( line_start , i - line_start ) ;
indent_stop = i ;
}
}
if ( has_indent & & indent_stop < 0 ) {
int j = i - line_start ;
2020-05-14 16:41:43 +02:00
if ( j > = indent . length ( ) | | c ! = indent [ j ] ) {
2017-10-11 10:27:54 +02:00
indent_stop = i ;
2020-05-14 16:41:43 +02:00
}
2017-10-11 10:27:54 +02:00
}
}
}
2020-05-14 16:41:43 +02:00
if ( has_text ) {
2017-10-11 10:27:54 +02:00
new_string + = substr ( indent_stop , length ( ) - indent_stop ) ;
2020-05-14 16:41:43 +02:00
}
2017-10-11 10:27:54 +02:00
return new_string ;
}
2016-05-11 09:22:59 +02:00
String String : : strip_edges ( bool left , bool right ) const {
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
int beg = 0 , end = len ;
2016-03-09 00:00:52 +01:00
2016-05-11 09:22:59 +02:00
if ( left ) {
for ( int i = 0 ; i < len ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < = 32 ) {
2016-05-11 09:22:59 +02:00
beg + + ;
2020-05-14 16:41:43 +02:00
} else {
2016-05-11 09:22:59 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-05-11 09:22:59 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2016-05-11 09:22:59 +02:00
if ( right ) {
2021-08-19 00:49:37 +02:00
for ( int i = len - 1 ; i > = 0 ; i - - ) {
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < = 32 ) {
2016-05-11 09:22:59 +02:00
end - - ;
2020-05-14 16:41:43 +02:00
} else {
2016-05-11 09:22:59 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-05-11 09:22:59 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( beg = = 0 & & end = = len ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return substr ( beg , end - beg ) ;
}
String String : : strip_escapes ( ) const {
2019-05-31 15:27:53 +02:00
String new_string ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < length ( ) ; i + + ) {
2019-05-31 15:27:53 +02:00
// Escape characters on first page of the ASCII table, before 32 (Space).
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < 32 ) {
2019-05-31 15:27:53 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2019-05-31 15:27:53 +02:00
new_string + = operator [ ] ( i ) ;
2014-02-10 02:10:30 +01:00
}
2019-05-31 15:27:53 +02:00
return new_string ;
2014-02-10 02:10:30 +01:00
}
2018-07-25 03:11:03 +02:00
String String : : lstrip ( const String & p_chars ) const {
2018-04-13 16:40:27 +02:00
int len = length ( ) ;
int beg ;
for ( beg = 0 ; beg < len ; beg + + ) {
2020-05-14 16:41:43 +02:00
if ( p_chars . find_char ( get ( beg ) ) = = - 1 ) {
2018-04-13 16:40:27 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
}
2020-05-14 16:41:43 +02:00
if ( beg = = 0 ) {
2018-04-13 16:40:27 +02:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
return substr ( beg , len - beg ) ;
}
2018-07-25 03:11:03 +02:00
String String : : rstrip ( const String & p_chars ) const {
2018-04-13 16:40:27 +02:00
int len = length ( ) ;
int end ;
for ( end = len - 1 ; end > = 0 ; end - - ) {
2020-05-14 16:41:43 +02:00
if ( p_chars . find_char ( get ( end ) ) = = - 1 ) {
2018-04-13 16:40:27 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
}
2020-05-14 16:41:43 +02:00
if ( end = = len - 1 ) {
2018-04-13 16:40:27 +02:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
return substr ( 0 , end + 1 ) ;
}
2022-01-24 12:12:46 +01:00
bool String : : is_network_share_path ( ) const {
return begins_with ( " // " ) | | begins_with ( " \\ \\ " ) ;
}
2014-02-10 02:10:30 +01:00
String String : : simplify_path ( ) const {
String s = * this ;
String drive ;
2022-10-11 14:40:12 +02:00
// Check if we have a special path (like res://) or a protocol identifier.
int p = s . find ( " :// " ) ;
bool found = false ;
if ( p > 0 ) {
bool only_chars = true ;
for ( int i = 0 ; i < p ; i + + ) {
2022-10-19 18:23:20 +02:00
if ( ! is_ascii_alphanumeric_char ( s [ i ] ) ) {
2022-10-11 14:40:12 +02:00
only_chars = false ;
break ;
}
2020-05-14 16:41:43 +02:00
}
2022-10-11 14:40:12 +02:00
if ( only_chars ) {
found = true ;
drive = s . substr ( 0 , p + 3 ) ;
s = s . substr ( p + 3 ) ;
}
}
if ( ! found ) {
if ( is_network_share_path ( ) ) {
// Network path, beginning with // or \\.
drive = s . substr ( 0 , 2 ) ;
s = s . substr ( 2 ) ;
} else if ( s . begins_with ( " / " ) | | s . begins_with ( " \\ " ) ) {
// Absolute path.
drive = s . substr ( 0 , 1 ) ;
s = s . substr ( 1 ) ;
} else {
// Windows-style drive path, like C:/ or C:\.
p = s . find ( " :/ " ) ;
if ( p = = - 1 ) {
p = s . find ( " : \\ " ) ;
}
if ( p ! = - 1 & & p < s . find ( " / " ) ) {
drive = s . substr ( 0 , p + 2 ) ;
s = s . substr ( p + 2 ) ;
}
2014-02-10 02:10:30 +01:00
}
}
s = s . replace ( " \\ " , " / " ) ;
2016-09-29 20:11:45 +02:00
while ( true ) { // in case of using 2 or more slash
String compare = s . replace ( " // " , " / " ) ;
2020-05-14 16:41:43 +02:00
if ( s = = compare ) {
2016-09-29 20:11:45 +02:00
break ;
2020-05-14 16:41:43 +02:00
} else {
2016-09-29 20:11:45 +02:00
s = compare ;
2020-05-14 16:41:43 +02:00
}
2016-09-29 20:11:45 +02:00
}
2014-02-10 02:10:30 +01:00
Vector < String > dirs = s . split ( " / " , false ) ;
for ( int i = 0 ; i < dirs . size ( ) ; i + + ) {
String d = dirs [ i ] ;
if ( d = = " . " ) {
2021-07-04 00:17:03 +02:00
dirs . remove_at ( i ) ;
2014-02-10 02:10:30 +01:00
i - - ;
} else if ( d = = " .. " ) {
if ( i = = 0 ) {
2021-07-04 00:17:03 +02:00
dirs . remove_at ( i ) ;
2014-02-10 02:10:30 +01:00
i - - ;
} else {
2021-07-04 00:17:03 +02:00
dirs . remove_at ( i ) ;
dirs . remove_at ( i - 1 ) ;
2014-02-10 02:10:30 +01:00
i - = 2 ;
}
}
}
s = " " ;
for ( int i = 0 ; i < dirs . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2014-02-10 02:10:30 +01:00
s + = " / " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
s + = dirs [ i ] ;
}
return drive + s ;
}
static int _humanize_digits ( int p_num ) {
2020-05-14 16:41:43 +02:00
if ( p_num < 100 ) {
2014-02-10 02:10:30 +01:00
return 2 ;
2020-05-14 16:41:43 +02:00
} else if ( p_num < 1024 ) {
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2019-10-04 13:35:01 +02:00
String String : : humanize_size ( uint64_t p_size ) {
2023-11-04 23:09:23 +01:00
int magnitude = 0 ;
2014-02-10 02:10:30 +01:00
uint64_t _div = 1 ;
2023-11-04 23:09:23 +01:00
while ( p_size > _div * 1024 & & magnitude < 6 ) {
2014-02-10 02:10:30 +01:00
_div * = 1024 ;
2023-11-04 23:09:23 +01:00
magnitude + + ;
2014-02-10 02:10:30 +01:00
}
2023-11-04 23:09:23 +01:00
if ( magnitude = = 0 ) {
return String : : num ( p_size ) + " " + RTR ( " B " ) ;
} else {
String suffix ;
switch ( magnitude ) {
case 1 :
suffix = RTR ( " KiB " ) ;
break ;
case 2 :
suffix = RTR ( " MiB " ) ;
break ;
case 3 :
suffix = RTR ( " GiB " ) ;
break ;
case 4 :
suffix = RTR ( " TiB " ) ;
break ;
case 5 :
suffix = RTR ( " PiB " ) ;
break ;
case 6 :
suffix = RTR ( " EiB " ) ;
break ;
}
2016-03-09 00:00:52 +01:00
2023-11-04 23:09:23 +01:00
const double divisor = _div ;
const int digits = _humanize_digits ( p_size / _div ) ;
return String : : num ( p_size / divisor ) . pad_decimals ( digits ) + " " + suffix ;
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2021-06-03 15:41:22 +02:00
bool String : : is_absolute_path ( ) const {
2020-05-14 16:41:43 +02:00
if ( length ( ) > 1 ) {
2014-02-10 02:10:30 +01:00
return ( operator [ ] ( 0 ) = = ' / ' | | operator [ ] ( 0 ) = = ' \\ ' | | find ( " :/ " ) ! = - 1 | | find ( " : \\ " ) ! = - 1 ) ;
2020-05-14 16:41:43 +02:00
} else if ( ( length ( ) ) = = 1 ) {
2014-02-10 02:10:30 +01:00
return ( operator [ ] ( 0 ) = = ' / ' | | operator [ ] ( 0 ) = = ' \\ ' ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-05-02 15:04:17 +02:00
String String : : validate_identifier ( ) const {
if ( is_empty ( ) ) {
return " _ " ; // Empty string is not a valid identifier;
}
2023-10-04 14:42:39 +02:00
String result ;
if ( is_digit ( operator [ ] ( 0 ) ) ) {
result = " _ " + * this ;
} else {
result = * this ;
}
2022-05-02 15:04:17 +02:00
int len = result . length ( ) ;
char32_t * buffer = result . ptrw ( ) ;
for ( int i = 0 ; i < len ; i + + ) {
2023-10-04 14:42:39 +02:00
if ( ! is_ascii_identifier_char ( buffer [ i ] ) ) {
2022-05-02 15:04:17 +02:00
buffer [ i ] = ' _ ' ;
}
}
return result ;
}
2014-02-10 02:10:30 +01:00
bool String : : is_valid_identifier ( ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-10-04 14:42:39 +02:00
if ( is_digit ( operator [ ] ( 0 ) ) ) {
return false ;
}
2020-07-27 12:43:20 +02:00
const char32_t * str = & operator [ ] ( 0 ) ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < len ; i + + ) {
2023-10-04 14:42:39 +02:00
if ( ! is_ascii_identifier_char ( str [ i ] ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool String : : is_valid_string ( ) const {
int l = length ( ) ;
const char32_t * src = get_data ( ) ;
bool valid = true ;
for ( int i = 0 ; i < l ; i + + ) {
valid = valid & & ( src [ i ] < 0xd800 | | ( src [ i ] > 0xdfff & & src [ i ] < = 0x10ffff ) ) ;
2015-08-26 04:00:11 +02:00
}
2020-07-27 12:43:20 +02:00
return valid ;
2015-08-26 04:00:11 +02:00
}
2020-11-30 04:43:38 +01:00
String String : : uri_encode ( ) const {
2015-11-18 12:33:29 +01:00
const CharString temp = utf8 ( ) ;
String res ;
2018-04-05 21:49:44 +02:00
for ( int i = 0 ; i < temp . length ( ) ; + + i ) {
2022-06-03 07:58:57 +02:00
uint8_t ord = temp [ i ] ;
2022-02-04 09:32:20 +01:00
if ( ord = = ' . ' | | ord = = ' - ' | | ord = = ' ~ ' | | is_ascii_identifier_char ( ord ) ) {
2015-11-18 12:33:29 +01:00
res + = ord ;
} else {
2022-06-03 07:58:57 +02:00
char p [ 4 ] = { ' % ' , 0 , 0 , 0 } ;
static const char hex [ 16 ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' } ;
p [ 1 ] = hex [ ord > > 4 ] ;
p [ 2 ] = hex [ ord & 0xF ] ;
res + = p ;
2015-11-18 12:33:29 +01:00
}
}
return res ;
}
2020-11-30 04:43:38 +01:00
String String : : uri_decode ( ) const {
2021-04-30 20:22:39 +02:00
CharString src = utf8 ( ) ;
CharString res ;
for ( int i = 0 ; i < src . length ( ) ; + + i ) {
if ( src [ i ] = = ' % ' & & i + 2 < src . length ( ) ) {
char ord1 = src [ i + 1 ] ;
2022-02-04 09:32:20 +01:00
if ( is_digit ( ord1 ) | | is_ascii_upper_case ( ord1 ) ) {
2021-04-30 20:22:39 +02:00
char ord2 = src [ i + 2 ] ;
2022-02-04 09:32:20 +01:00
if ( is_digit ( ord2 ) | | is_ascii_upper_case ( ord2 ) ) {
2019-07-08 17:15:10 +02:00
char bytes [ 3 ] = { ( char ) ord1 , ( char ) ord2 , 0 } ;
2020-04-02 01:20:12 +02:00
res + = ( char ) strtol ( bytes , nullptr , 16 ) ;
2015-11-18 12:33:29 +01:00
i + = 2 ;
}
} else {
2021-04-30 20:22:39 +02:00
res + = src [ i ] ;
2015-11-18 12:33:29 +01:00
}
2021-04-30 20:22:39 +02:00
} else if ( src [ i ] = = ' + ' ) {
2020-11-30 04:43:38 +01:00
res + = ' ' ;
2015-11-18 12:33:29 +01:00
} else {
2021-04-30 20:22:39 +02:00
res + = src [ i ] ;
2015-11-18 12:33:29 +01:00
}
}
2021-04-30 20:22:39 +02:00
return String : : utf8 ( res ) ;
2015-11-18 12:33:29 +01:00
}
2014-02-10 02:10:30 +01:00
String String : : c_unescape ( ) const {
String escaped = * this ;
escaped = escaped . replace ( " \\ a " , " \a " ) ;
escaped = escaped . replace ( " \\ b " , " \b " ) ;
escaped = escaped . replace ( " \\ f " , " \f " ) ;
escaped = escaped . replace ( " \\ n " , " \n " ) ;
escaped = escaped . replace ( " \\ r " , " \r " ) ;
escaped = escaped . replace ( " \\ t " , " \t " ) ;
escaped = escaped . replace ( " \\ v " , " \v " ) ;
escaped = escaped . replace ( " \\ ' " , " \' " ) ;
escaped = escaped . replace ( " \\ \" " , " \" " ) ;
escaped = escaped . replace ( " \\ \\ " , " \\ " ) ;
return escaped ;
}
String String : : c_escape ( ) const {
String escaped = * this ;
escaped = escaped . replace ( " \\ " , " \\ \\ " ) ;
escaped = escaped . replace ( " \a " , " \\ a " ) ;
escaped = escaped . replace ( " \b " , " \\ b " ) ;
escaped = escaped . replace ( " \f " , " \\ f " ) ;
escaped = escaped . replace ( " \n " , " \\ n " ) ;
escaped = escaped . replace ( " \r " , " \\ r " ) ;
escaped = escaped . replace ( " \t " , " \\ t " ) ;
escaped = escaped . replace ( " \v " , " \\ v " ) ;
escaped = escaped . replace ( " \' " , " \\ ' " ) ;
2017-01-16 18:03:38 +01:00
escaped = escaped . replace ( " \" " , " \\ \" " ) ;
return escaped ;
}
String String : : c_escape_multiline ( ) const {
String escaped = * this ;
escaped = escaped . replace ( " \\ " , " \\ \\ " ) ;
escaped = escaped . replace ( " \" " , " \\ \" " ) ;
2014-02-10 02:10:30 +01:00
return escaped ;
}
2016-01-10 19:01:06 +01:00
String String : : json_escape ( ) const {
String escaped = * this ;
escaped = escaped . replace ( " \\ " , " \\ \\ " ) ;
escaped = escaped . replace ( " \b " , " \\ b " ) ;
escaped = escaped . replace ( " \f " , " \\ f " ) ;
escaped = escaped . replace ( " \n " , " \\ n " ) ;
escaped = escaped . replace ( " \r " , " \\ r " ) ;
escaped = escaped . replace ( " \t " , " \\ t " ) ;
escaped = escaped . replace ( " \v " , " \\ v " ) ;
escaped = escaped . replace ( " \" " , " \\ \" " ) ;
return escaped ;
}
2014-02-10 02:10:30 +01:00
String String : : xml_escape ( bool p_escape_quotes ) const {
String str = * this ;
str = str . replace ( " & " , " & " ) ;
2015-09-23 20:33:31 +02:00
str = str . replace ( " < " , " < " ) ;
str = str . replace ( " > " , " > " ) ;
2014-02-10 02:10:30 +01:00
if ( p_escape_quotes ) {
str = str . replace ( " ' " , " ' " ) ;
str = str . replace ( " \" " , " " " ) ;
}
/*
2020-05-14 16:41:43 +02:00
for ( int i = 1 ; i < 32 ; i + + ) {
char chr [ 2 ] = { i , 0 } ;
str = str . replace ( chr , " &# " + String : : num ( i ) + " ; " ) ;
} */
2014-02-10 02:10:30 +01:00
return str ;
}
2020-07-27 12:43:20 +02:00
static _FORCE_INLINE_ int _xml_unescape ( const char32_t * p_src , int p_src_len , char32_t * p_dst ) {
2014-06-11 15:41:03 +02:00
int len = 0 ;
while ( p_src_len ) {
if ( * p_src = = ' & ' ) {
int eat = 0 ;
if ( p_src_len > = 4 & & p_src [ 1 ] = = ' # ' ) {
2020-07-27 12:43:20 +02:00
char32_t c = 0 ;
2021-02-11 16:33:55 +01:00
bool overflow = false ;
if ( p_src [ 2 ] = = ' x ' ) {
// Hex entity &#x<num>;
for ( int i = 3 ; i < p_src_len ; i + + ) {
eat = i + 1 ;
char32_t ct = p_src [ i ] ;
if ( ct = = ' ; ' ) {
break ;
2021-08-12 19:48:42 +02:00
} else if ( is_digit ( ct ) ) {
2021-02-11 16:33:55 +01:00
ct = ct - ' 0 ' ;
} else if ( ct > = ' a ' & & ct < = ' f ' ) {
ct = ( ct - ' a ' ) + 10 ;
} else if ( ct > = ' A ' & & ct < = ' F ' ) {
ct = ( ct - ' A ' ) + 10 ;
} else {
break ;
}
if ( c > ( UINT32_MAX > > 4 ) ) {
overflow = true ;
break ;
}
c < < = 4 ;
c | = ct ;
}
} else {
// Decimal entity &#<num>;
for ( int i = 2 ; i < p_src_len ; i + + ) {
eat = i + 1 ;
char32_t ct = p_src [ i ] ;
2022-02-04 09:32:20 +01:00
if ( ct = = ' ; ' | | ! is_digit ( ct ) ) {
2021-02-11 16:33:55 +01:00
break ;
}
}
if ( p_src [ eat - 1 ] = = ' ; ' ) {
int64_t val = String : : to_int ( p_src + 2 , eat - 3 ) ;
if ( val > 0 & & val < = UINT32_MAX ) {
c = ( char32_t ) val ;
} else {
overflow = true ;
}
2014-06-11 15:41:03 +02:00
}
}
2021-02-11 16:33:55 +01:00
// Value must be non-zero, in the range of char32_t,
// actually end with ';'. If invalid, leave the entity as-is
if ( c = = ' \0 ' | | overflow | | p_src [ eat - 1 ] ! = ' ; ' ) {
eat = 1 ;
c = * p_src ;
}
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
* p_dst = c ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
} else if ( p_src_len > = 4 & & p_src [ 1 ] = = ' g ' & & p_src [ 2 ] = = ' t ' & & p_src [ 3 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2015-09-23 20:40:24 +02:00
* p_dst = ' > ' ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
eat = 4 ;
} else if ( p_src_len > = 4 & & p_src [ 1 ] = = ' l ' & & p_src [ 2 ] = = ' t ' & & p_src [ 3 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2015-09-23 20:40:24 +02:00
* p_dst = ' < ' ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
eat = 4 ;
} else if ( p_src_len > = 5 & & p_src [ 1 ] = = ' a ' & & p_src [ 2 ] = = ' m ' & & p_src [ 3 ] = = ' p ' & & p_src [ 4 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
* p_dst = ' & ' ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
eat = 5 ;
} else if ( p_src_len > = 6 & & p_src [ 1 ] = = ' q ' & & p_src [ 2 ] = = ' u ' & & p_src [ 3 ] = = ' o ' & & p_src [ 4 ] = = ' t ' & & p_src [ 5 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
* p_dst = ' " ' ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
eat = 6 ;
} else if ( p_src_len > = 6 & & p_src [ 1 ] = = ' a ' & & p_src [ 2 ] = = ' p ' & & p_src [ 3 ] = = ' o ' & & p_src [ 4 ] = = ' s ' & & p_src [ 5 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
* p_dst = ' \' ' ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
eat = 6 ;
} else {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
* p_dst = * p_src ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
eat = 1 ;
}
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
p_dst + + ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
len + + ;
p_src + = eat ;
p_src_len - = eat ;
} else {
if ( p_dst ) {
* p_dst = * p_src ;
p_dst + + ;
}
len + + ;
p_src + + ;
p_src_len - - ;
}
}
return len ;
}
2014-02-10 02:10:30 +01:00
2014-06-11 15:41:03 +02:00
String String : : xml_unescape ( ) const {
String str ;
int l = length ( ) ;
2020-07-27 12:43:20 +02:00
int len = _xml_unescape ( get_data ( ) , l , nullptr ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-06-11 15:41:03 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
str . resize ( len + 1 ) ;
2020-07-27 12:43:20 +02:00
_xml_unescape ( get_data ( ) , l , str . ptrw ( ) ) ;
2014-06-11 15:41:03 +02:00
str [ len ] = 0 ;
2014-02-10 02:10:30 +01:00
return str ;
}
String String : : pad_decimals ( int p_digits ) const {
String s = * this ;
int c = s . find ( " . " ) ;
if ( c = = - 1 ) {
if ( p_digits < = 0 ) {
return s ;
}
s + = " . " ;
c = s . length ( ) - 1 ;
} else {
if ( p_digits < = 0 ) {
return s . substr ( 0 , c ) ;
}
}
if ( s . length ( ) - ( c + 1 ) > p_digits ) {
2023-01-29 00:52:05 +01:00
return s . substr ( 0 , c + p_digits + 1 ) ;
2014-02-10 02:10:30 +01:00
} else {
2023-01-29 00:52:05 +01:00
int zeros_to_add = p_digits - s . length ( ) + ( c + 1 ) ;
return s + String ( " 0 " ) . repeat ( zeros_to_add ) ;
2014-02-10 02:10:30 +01:00
}
}
String String : : pad_zeros ( int p_digits ) const {
String s = * this ;
int end = s . find ( " . " ) ;
if ( end = = - 1 ) {
end = s . length ( ) ;
}
2020-05-14 16:41:43 +02:00
if ( end = = 0 ) {
2014-02-10 02:10:30 +01:00
return s ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int begin = 0 ;
2022-02-04 09:32:20 +01:00
while ( begin < end & & ! is_digit ( s [ begin ] ) ) {
2014-02-10 02:10:30 +01:00
begin + + ;
}
2023-07-08 14:54:05 +02:00
int zeros_to_add = p_digits - ( end - begin ) ;
if ( zeros_to_add < = 0 ) {
2014-02-10 02:10:30 +01:00
return s ;
2023-07-08 14:54:05 +02:00
} else {
return s . insert ( begin , String ( " 0 " ) . repeat ( zeros_to_add ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2018-04-13 16:40:27 +02:00
String String : : trim_prefix ( const String & p_prefix ) const {
String s = * this ;
if ( s . begins_with ( p_prefix ) ) {
return s . substr ( p_prefix . length ( ) , s . length ( ) - p_prefix . length ( ) ) ;
}
return s ;
}
String String : : trim_suffix ( const String & p_suffix ) const {
String s = * this ;
if ( s . ends_with ( p_suffix ) ) {
return s . substr ( 0 , s . length ( ) - p_suffix . length ( ) ) ;
}
return s ;
}
2021-06-16 18:24:34 +02:00
bool String : : is_valid_int ( ) const {
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int from = 0 ;
2020-05-14 16:41:43 +02:00
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) ) {
2014-02-10 02:10:30 +01:00
from + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
for ( int i = from ; i < len ; i + + ) {
2022-02-04 09:32:20 +01:00
if ( ! is_digit ( operator [ ] ( i ) ) ) {
2014-02-10 02:10:30 +01:00
return false ; // no start with number plz
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2016-10-20 14:58:00 +02:00
bool String : : is_valid_hex_number ( bool p_with_prefix ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-12-24 19:06:35 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-12-24 19:06:35 +01:00
int from = 0 ;
2020-05-14 16:41:43 +02:00
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) ) {
2016-10-20 14:58:00 +02:00
from + + ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
if ( p_with_prefix ) {
2020-05-14 16:41:43 +02:00
if ( len < 3 ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
if ( operator [ ] ( from ) ! = ' 0 ' | | operator [ ] ( from + 1 ) ! = ' x ' ) {
return false ;
2018-04-18 22:20:39 +02:00
}
2016-10-20 14:58:00 +02:00
from + = 2 ;
2018-04-18 22:20:39 +02:00
}
2016-10-20 14:58:00 +02:00
for ( int i = from ; i < len ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2021-08-12 19:48:42 +02:00
if ( is_hex_digit ( c ) ) {
2016-10-20 14:58:00 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
return false ;
2018-04-18 22:20:39 +02:00
}
2016-10-20 14:58:00 +02:00
return true ;
2020-05-19 15:46:49 +02:00
}
2016-10-20 14:58:00 +02:00
2014-02-10 02:10:30 +01:00
bool String : : is_valid_float ( ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
int from = 0 ;
if ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) {
from + + ;
}
bool exponent_found = false ;
bool period_found = false ;
bool sign_found = false ;
bool exponent_values_found = false ;
bool numbers_found = false ;
for ( int i = from ; i < len ; i + + ) {
2021-08-12 19:48:42 +02:00
if ( is_digit ( operator [ ] ( i ) ) ) {
2020-05-14 16:41:43 +02:00
if ( exponent_found ) {
2014-02-10 02:10:30 +01:00
exponent_values_found = true ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
numbers_found = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} else if ( numbers_found & & ! exponent_found & & operator [ ] ( i ) = = ' e ' ) {
exponent_found = true ;
} else if ( ! period_found & & ! exponent_found & & operator [ ] ( i ) = = ' . ' ) {
period_found = true ;
} else if ( ( operator [ ] ( i ) = = ' - ' | | operator [ ] ( i ) = = ' + ' ) & & exponent_found & & ! exponent_values_found & & ! sign_found ) {
sign_found = true ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ; // no start with number plz
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return numbers_found ;
}
String String : : path_to_file ( const String & p_path ) const {
2019-12-14 11:06:29 +01:00
// Don't get base dir for src, this is expected to be a dir already.
2024-01-28 21:51:39 +01:00
String src = replace ( " \\ " , " / " ) ;
2014-02-10 02:10:30 +01:00
String dst = p_path . replace ( " \\ " , " / " ) . get_base_dir ( ) ;
2015-05-04 20:06:41 +02:00
String rel = src . path_to ( dst ) ;
2020-05-14 16:41:43 +02:00
if ( rel = = dst ) { // failed
2015-05-04 20:06:41 +02:00
return p_path ;
2020-05-14 16:41:43 +02:00
} else {
2015-05-04 20:06:41 +02:00
return rel + p_path . get_file ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
String String : : path_to ( const String & p_path ) const {
2024-01-28 21:51:39 +01:00
String src = replace ( " \\ " , " / " ) ;
2014-02-10 02:10:30 +01:00
String dst = p_path . replace ( " \\ " , " / " ) ;
2020-05-14 16:41:43 +02:00
if ( ! src . ends_with ( " / " ) ) {
2014-02-10 02:10:30 +01:00
src + = " / " ;
2020-05-14 16:41:43 +02:00
}
if ( ! dst . ends_with ( " / " ) ) {
2014-02-10 02:10:30 +01:00
dst + = " / " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
if ( src . begins_with ( " res:// " ) & & dst . begins_with ( " res:// " ) ) {
src = src . replace ( " res:// " , " / " ) ;
dst = dst . replace ( " res:// " , " / " ) ;
} else if ( src . begins_with ( " user:// " ) & & dst . begins_with ( " user:// " ) ) {
src = src . replace ( " user:// " , " / " ) ;
dst = dst . replace ( " user:// " , " / " ) ;
} else if ( src . begins_with ( " / " ) & & dst . begins_with ( " / " ) ) {
//nothing
} else {
//dos style
2015-06-29 05:29:49 +02:00
String src_begin = src . get_slicec ( ' / ' , 0 ) ;
String dst_begin = dst . get_slicec ( ' / ' , 0 ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( src_begin ! = dst_begin ) {
2015-05-04 20:06:41 +02:00
return p_path ; //impossible to do this
2020-05-14 16:41:43 +02:00
}
2015-05-04 20:06:41 +02:00
2014-02-10 02:10:30 +01:00
src = src . substr ( src_begin . length ( ) , src . length ( ) ) ;
dst = dst . substr ( dst_begin . length ( ) , dst . length ( ) ) ;
}
//remove leading and trailing slash and split
Vector < String > src_dirs = src . substr ( 1 , src . length ( ) - 2 ) . split ( " / " ) ;
Vector < String > dst_dirs = dst . substr ( 1 , dst . length ( ) - 2 ) . split ( " / " ) ;
//find common parent
int common_parent = 0 ;
while ( true ) {
2020-05-14 16:41:43 +02:00
if ( src_dirs . size ( ) = = common_parent ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
if ( dst_dirs . size ( ) = = common_parent ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
if ( src_dirs [ common_parent ] ! = dst_dirs [ common_parent ] ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
common_parent + + ;
}
common_parent - - ;
2023-01-29 00:52:05 +01:00
int dirs_to_backtrack = ( src_dirs . size ( ) - 1 ) - common_parent ;
String dir = String ( " ../ " ) . repeat ( dirs_to_backtrack ) ;
2014-02-10 02:10:30 +01:00
for ( int i = common_parent + 1 ; i < dst_dirs . size ( ) ; i + + ) {
dir + = dst_dirs [ i ] + " / " ;
}
2020-05-14 16:41:43 +02:00
if ( dir . length ( ) = = 0 ) {
2014-02-10 02:10:30 +01:00
dir = " ./ " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return dir ;
}
bool String : : is_valid_html_color ( ) const {
return Color : : html_is_valid ( * this ) ;
}
2022-07-19 02:58:27 +02:00
// Changes made to the set of invalid filename characters must also be reflected in the String documentation for is_valid_filename.
static const char * invalid_filename_characters = " : / \\ ? * \" | % < > " ;
2019-04-09 00:18:03 +02:00
bool String : : is_valid_filename ( ) const {
String stripped = strip_edges ( ) ;
if ( * this ! = stripped ) {
return false ;
}
2021-12-09 10:42:46 +01:00
if ( stripped . is_empty ( ) ) {
2019-04-09 00:18:03 +02:00
return false ;
}
2022-07-19 02:58:27 +02:00
Vector < String > chars = String ( invalid_filename_characters ) . split ( " " ) ;
for ( const String & ch : chars ) {
if ( contains ( ch ) ) {
return false ;
}
}
return true ;
}
String String : : validate_filename ( ) const {
Vector < String > chars = String ( invalid_filename_characters ) . split ( " " ) ;
String name = strip_edges ( ) ;
for ( int i = 0 ; i < chars . size ( ) ; i + + ) {
name = name . replace ( chars [ i ] , " _ " ) ;
}
return name ;
2019-04-09 00:18:03 +02:00
}
2014-02-10 02:10:30 +01:00
bool String : : is_valid_ip_address ( ) const {
2016-10-20 14:58:00 +02:00
if ( find ( " : " ) > = 0 ) {
Vector < String > ip = split ( " : " ) ;
for ( int i = 0 ; i < ip . size ( ) ; i + + ) {
2023-11-18 23:40:56 +01:00
const String & n = ip [ i ] ;
2020-12-15 13:04:21 +01:00
if ( n . is_empty ( ) ) {
2016-10-20 14:58:00 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
if ( n . is_valid_hex_number ( false ) ) {
2021-01-28 13:39:05 +01:00
int64_t nint = n . hex_to_int ( ) ;
2020-05-14 16:41:43 +02:00
if ( nint < 0 | | nint > 0xffff ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
continue ;
2020-05-19 15:46:49 +02:00
}
2020-05-14 16:41:43 +02:00
if ( ! n . is_valid_ip_address ( ) ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-05-19 15:46:49 +02:00
}
2016-10-20 14:58:00 +02:00
} else {
Vector < String > ip = split ( " . " ) ;
2020-05-14 16:41:43 +02:00
if ( ip . size ( ) ! = 4 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
for ( int i = 0 ; i < ip . size ( ) ; i + + ) {
2023-11-18 23:40:56 +01:00
const String & n = ip [ i ] ;
2021-06-16 18:24:34 +02:00
if ( ! n . is_valid_int ( ) ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
int val = n . to_int ( ) ;
2020-05-14 16:41:43 +02:00
if ( val < 0 | | val > 255 ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
}
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
bool String : : is_resource_file ( ) const {
return begins_with ( " res:// " ) & & find ( " :: " ) = = - 1 ;
}
2021-08-30 01:43:47 +02:00
bool String : : is_relative_path ( ) const {
2021-06-03 15:41:22 +02:00
return ! is_absolute_path ( ) ;
2014-02-10 02:10:30 +01:00
}
String String : : get_base_dir ( ) const {
2021-08-24 05:32:13 +02:00
int end = 0 ;
2022-01-24 12:12:46 +01:00
// URL scheme style base.
2021-08-24 05:32:13 +02:00
int basepos = find ( " :// " ) ;
if ( basepos ! = - 1 ) {
end = basepos + 3 ;
2020-12-23 09:48:18 +01:00
}
2021-08-24 05:32:13 +02:00
2022-01-24 12:12:46 +01:00
// Windows top level directory base.
2021-08-24 05:32:13 +02:00
if ( end = = 0 ) {
basepos = find ( " :/ " ) ;
if ( basepos = = - 1 ) {
basepos = find ( " : \\ " ) ;
}
if ( basepos ! = - 1 ) {
end = basepos + 2 ;
}
}
2022-01-24 12:12:46 +01:00
// Windows UNC network share path.
if ( end = = 0 ) {
if ( is_network_share_path ( ) ) {
basepos = find ( " / " , 2 ) ;
if ( basepos = = - 1 ) {
basepos = find ( " \\ " , 2 ) ;
}
int servpos = find ( " / " , basepos + 1 ) ;
if ( servpos = = - 1 ) {
servpos = find ( " \\ " , basepos + 1 ) ;
}
if ( servpos ! = - 1 ) {
end = servpos + 1 ;
}
}
}
// Unix root directory base.
2021-08-24 05:32:13 +02:00
if ( end = = 0 ) {
if ( begins_with ( " / " ) ) {
end = 1 ;
}
}
2014-02-10 02:10:30 +01:00
String rs ;
String base ;
2021-08-24 05:32:13 +02:00
if ( end ! = 0 ) {
2014-02-10 02:10:30 +01:00
rs = substr ( end , length ( ) ) ;
base = substr ( 0 , end ) ;
} else {
2021-08-24 05:32:13 +02:00
rs = * this ;
2014-02-10 02:10:30 +01:00
}
2020-07-03 15:26:22 +02:00
int sep = MAX ( rs . rfind ( " / " ) , rs . rfind ( " \\ " ) ) ;
2020-05-14 16:41:43 +02:00
if ( sep = = - 1 ) {
2014-02-10 02:10:30 +01:00
return base ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return base + rs . substr ( 0 , sep ) ;
}
String String : : get_file ( ) const {
2020-07-03 15:26:22 +02:00
int sep = MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ;
2020-05-14 16:41:43 +02:00
if ( sep = = - 1 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return substr ( sep + 1 , length ( ) ) ;
}
2017-01-14 04:51:09 +01:00
String String : : get_extension ( ) const {
2020-07-03 15:26:22 +02:00
int pos = rfind ( " . " ) ;
if ( pos < 0 | | pos < MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ) {
2018-01-11 12:59:31 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
return substr ( pos + 1 , length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-08-30 02:34:01 +02:00
String String : : path_join ( const String & p_file ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2017-01-14 17:47:07 +01:00
return p_file ;
2020-05-14 16:41:43 +02:00
}
if ( operator [ ] ( length ( ) - 1 ) = = ' / ' | | ( p_file . size ( ) > 0 & & p_file . operator [ ] ( 0 ) = = ' / ' ) ) {
2014-02-10 02:10:30 +01:00
return * this + p_file ;
2020-05-14 16:41:43 +02:00
}
2017-01-16 19:20:33 +01:00
return * this + " / " + p_file ;
2014-02-10 02:10:30 +01:00
}
2019-12-20 03:03:15 +01:00
String String : : property_name_encode ( ) const {
// Escape and quote strings with extended ASCII or further Unicode characters
// as well as '"', '=' or ' ' (32)
2020-07-27 12:43:20 +02:00
const char32_t * cstr = get_data ( ) ;
2019-12-20 03:03:15 +01:00
for ( int i = 0 ; cstr [ i ] ; i + + ) {
2021-11-11 06:18:09 +01:00
if ( cstr [ i ] = = ' = ' | | cstr [ i ] = = ' " ' | | cstr [ i ] = = ' ; ' | | cstr [ i ] = = ' [ ' | | cstr [ i ] = = ' ] ' | | cstr [ i ] < 33 | | cstr [ i ] > 126 ) {
2019-12-20 03:03:15 +01:00
return " \" " + c_escape_multiline ( ) + " \" " ;
}
}
// Keep as is
return * this ;
}
2021-01-28 21:48:12 +01:00
// Changes made to the set of invalid characters must also be reflected in the String documentation.
2023-04-06 17:54:56 +02:00
static const char32_t invalid_node_name_characters [ ] = { ' . ' , ' : ' , ' @ ' , ' / ' , ' \" ' , UNIQUE_NODE_PREFIX [ 0 ] , 0 } ;
2023-11-09 15:37:08 +01:00
String String : : get_invalid_node_name_characters ( bool p_allow_internal ) {
2023-04-06 17:54:56 +02:00
// Do not use this function for critical validation.
String r ;
const char32_t * c = invalid_node_name_characters ;
while ( * c ) {
2023-11-09 15:37:08 +01:00
if ( p_allow_internal & & * c = = ' @ ' ) {
c + + ;
continue ;
}
2023-04-06 17:54:56 +02:00
if ( c ! = invalid_node_name_characters ) {
r + = " " ;
}
r + = String : : chr ( * c ) ;
c + + ;
}
return r ;
}
2021-01-28 21:48:12 +01:00
String String : : validate_node_name ( ) const {
2023-04-06 17:54:56 +02:00
// This is a critical validation in node addition, so it must be optimized.
const char32_t * cn = ptr ( ) ;
if ( cn = = nullptr ) {
return String ( ) ;
2021-01-28 21:48:12 +01:00
}
2023-04-06 17:54:56 +02:00
bool valid = true ;
uint32_t idx = 0 ;
while ( cn [ idx ] ) {
const char32_t * c = invalid_node_name_characters ;
while ( * c ) {
if ( cn [ idx ] = = * c ) {
valid = false ;
break ;
}
c + + ;
}
if ( ! valid ) {
break ;
}
idx + + ;
}
if ( valid ) {
return * this ;
}
String validated = * this ;
char32_t * nn = validated . ptrw ( ) ;
while ( nn [ idx ] ) {
const char32_t * c = invalid_node_name_characters ;
while ( * c ) {
if ( nn [ idx ] = = * c ) {
nn [ idx ] = ' _ ' ;
break ;
}
c + + ;
}
idx + + ;
}
return validated ;
2021-01-28 21:48:12 +01:00
}
2017-01-14 04:51:09 +01:00
String String : : get_basename ( ) const {
2020-07-03 15:26:22 +02:00
int pos = rfind ( " . " ) ;
if ( pos < 0 | | pos < MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return substr ( 0 , pos ) ;
}
String itos ( int64_t p_val ) {
return String : : num_int64 ( p_val ) ;
}
2019-11-01 16:16:31 +01:00
String uitos ( uint64_t p_val ) {
return String : : num_uint64 ( p_val ) ;
}
2014-02-10 02:10:30 +01:00
String rtos ( double p_val ) {
return String : : num ( p_val ) ;
}
String rtoss ( double p_val ) {
return String : : num_scientific ( p_val ) ;
}
2015-02-01 19:42:36 +01:00
// Right-pad with a character.
String String : : rpad ( int min_length , const String & character ) const {
String s = * this ;
int padding = min_length - s . length ( ) ;
if ( padding > 0 ) {
2023-01-29 00:52:05 +01:00
s + = character . repeat ( padding ) ;
2015-02-01 19:42:36 +01:00
}
return s ;
}
2020-05-14 14:29:06 +02:00
2015-02-01 19:42:36 +01:00
// Left-pad with a character.
String String : : lpad ( int min_length , const String & character ) const {
String s = * this ;
int padding = min_length - s . length ( ) ;
if ( padding > 0 ) {
2023-01-29 00:52:05 +01:00
s = character . repeat ( padding ) + s ;
2015-02-01 19:42:36 +01:00
}
return s ;
}
2015-01-10 21:44:20 +01:00
// sprintf is implemented in GDScript via:
// "fish %s pie" % "frog"
// "fish %s %d pie" % ["frog", 12]
2015-02-19 16:45:49 +01:00
// In case of an error, the string returned is the error description and "error" is true.
String String : : sprintf ( const Array & values , bool * error ) const {
2015-01-10 21:44:20 +01:00
String formatted ;
2020-07-27 12:43:20 +02:00
char32_t * self = ( char32_t * ) get_data ( ) ;
2015-01-10 21:44:20 +01:00
bool in_format = false ;
int value_index = 0 ;
2017-09-01 22:33:39 +02:00
int min_chars = 0 ;
int min_decimals = 0 ;
bool in_decimals = false ;
2022-05-01 05:04:52 +02:00
bool pad_with_zeros = false ;
2017-09-01 22:33:39 +02:00
bool left_justified = false ;
bool show_sign = false ;
2023-04-18 13:33:30 +02:00
bool as_unsigned = false ;
2015-01-10 21:44:20 +01:00
2020-11-05 03:01:55 +01:00
if ( error ) {
* error = true ;
}
2015-02-01 21:18:38 +01:00
2015-01-10 21:44:20 +01:00
for ( ; * self ; self + + ) {
2020-07-27 12:43:20 +02:00
const char32_t c = * self ;
2015-01-10 21:44:20 +01:00
2021-03-12 14:35:16 +01:00
if ( in_format ) { // We have % - let's see what else we get.
2015-01-10 21:44:20 +01:00
switch ( c ) {
2015-02-01 19:42:36 +01:00
case ' % ' : { // Replace %% with %
2015-01-10 21:44:20 +01:00
formatted + = chr ( c ) ;
in_format = false ;
break ;
2015-02-01 19:42:36 +01:00
}
2015-01-10 21:44:20 +01:00
case ' d ' : // Integer (signed)
case ' o ' : // Octal
case ' x ' : // Hexadecimal (lowercase)
2015-02-01 19:42:36 +01:00
case ' X ' : { // Hexadecimal (uppercase)
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
if ( ! values [ value_index ] . is_num ( ) ) {
2015-02-19 16:45:49 +01:00
return " a number is required " ;
2015-02-01 21:18:38 +01:00
}
2016-03-09 00:00:52 +01:00
2015-02-01 21:18:38 +01:00
int64_t value = values [ value_index ] ;
2017-09-01 22:33:39 +02:00
int base = 16 ;
2015-02-01 21:18:38 +01:00
bool capitalize = false ;
switch ( c ) {
2020-05-10 13:00:47 +02:00
case ' d ' :
base = 10 ;
break ;
case ' o ' :
base = 8 ;
break ;
case ' x ' :
break ;
2015-02-01 21:18:38 +01:00
case ' X ' :
capitalize = true ;
break ;
}
// Get basic number.
2023-04-18 13:33:30 +02:00
String str ;
if ( ! as_unsigned ) {
str = String : : num_int64 ( ABS ( value ) , base , capitalize ) ;
} else {
uint64_t uvalue = * ( ( uint64_t * ) & value ) ;
// In unsigned hex, if the value fits in 32 bits, trim it down to that.
if ( base = = 16 & & value < 0 & & value > = INT32_MIN ) {
uvalue & = 0xffffffff ;
}
str = String : : num_uint64 ( uvalue , base , capitalize ) ;
}
2017-02-08 02:40:35 +01:00
int number_len = str . length ( ) ;
2015-02-01 19:42:36 +01:00
2023-04-18 13:33:30 +02:00
bool negative = value < 0 & & ! as_unsigned ;
2015-02-01 21:18:38 +01:00
// Padding.
2023-04-18 13:33:30 +02:00
int pad_chars_count = ( negative | | show_sign ) ? min_chars - 1 : min_chars ;
2022-05-01 05:04:52 +02:00
String pad_char = pad_with_zeros ? String ( " 0 " ) : String ( " " ) ;
2015-02-01 21:18:38 +01:00
if ( left_justified ) {
2020-10-01 22:06:12 +02:00
str = str . rpad ( pad_chars_count , pad_char ) ;
2015-01-10 21:44:20 +01:00
} else {
2020-10-01 22:06:12 +02:00
str = str . lpad ( pad_chars_count , pad_char ) ;
2015-01-10 21:44:20 +01:00
}
2015-02-01 21:18:38 +01:00
2017-02-08 02:40:35 +01:00
// Sign.
2023-04-18 13:33:30 +02:00
if ( show_sign | | negative ) {
String sign_char = negative ? " - " : " + " ;
2022-05-01 05:04:52 +02:00
if ( left_justified ) {
str = str . insert ( 0 , sign_char ) ;
} else {
str = str . insert ( pad_with_zeros ? 0 : str . length ( ) - number_len , sign_char ) ;
}
2017-02-08 02:40:35 +01:00
}
2015-02-01 21:18:38 +01:00
formatted + = str ;
+ + value_index ;
in_format = false ;
2015-01-10 21:44:20 +01:00
break ;
2015-02-01 19:42:36 +01:00
}
case ' f ' : { // Float
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
if ( ! values [ value_index ] . is_num ( ) ) {
2015-02-19 16:45:49 +01:00
return " a number is required " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
double value = values [ value_index ] ;
2023-09-05 08:31:29 +02:00
bool is_negative = signbit ( value ) ;
String str = String : : num ( Math : : abs ( value ) , min_decimals ) ;
2022-08-11 10:12:27 +02:00
const bool is_finite = Math : : is_finite ( value ) ;
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
// Pad decimals out.
2022-08-11 10:12:27 +02:00
if ( is_finite ) {
2022-08-24 15:41:31 +02:00
str = str . pad_decimals ( min_decimals ) ;
}
2015-02-01 21:18:38 +01:00
2020-06-03 13:47:47 +02:00
int initial_len = str . length ( ) ;
2015-01-10 21:44:20 +01:00
2020-06-03 13:47:47 +02:00
// Padding. Leave room for sign later if required.
int pad_chars_count = ( is_negative | | show_sign ) ? min_chars - 1 : min_chars ;
2022-08-11 10:12:27 +02:00
String pad_char = ( pad_with_zeros & & is_finite ) ? String ( " 0 " ) : String ( " " ) ; // Never pad NaN or inf with zeros
2015-02-01 21:18:38 +01:00
if ( left_justified ) {
2022-05-01 05:04:52 +02:00
str = str . rpad ( pad_chars_count , pad_char ) ;
2015-01-10 21:44:20 +01:00
} else {
2020-06-03 13:47:47 +02:00
str = str . lpad ( pad_chars_count , pad_char ) ;
}
// Add sign if needed.
if ( show_sign | | is_negative ) {
String sign_char = is_negative ? " - " : " + " ;
if ( left_justified ) {
str = str . insert ( 0 , sign_char ) ;
} else {
2022-05-01 05:04:52 +02:00
str = str . insert ( pad_with_zeros ? 0 : str . length ( ) - initial_len , sign_char ) ;
2020-06-03 13:47:47 +02:00
}
2015-01-10 21:44:20 +01:00
}
2015-02-01 21:18:38 +01:00
formatted + = str ;
+ + value_index ;
in_format = false ;
2015-01-10 21:44:20 +01:00
break ;
2015-02-01 19:42:36 +01:00
}
2022-07-31 17:50:02 +02:00
case ' v ' : { // Vector2/3/4/2i/3i/4i
if ( value_index > = values . size ( ) ) {
return " not enough arguments for format string " ;
}
int count ;
switch ( values [ value_index ] . get_type ( ) ) {
case Variant : : VECTOR2 :
case Variant : : VECTOR2I : {
count = 2 ;
} break ;
case Variant : : VECTOR3 :
case Variant : : VECTOR3I : {
count = 3 ;
} break ;
case Variant : : VECTOR4 :
case Variant : : VECTOR4I : {
count = 4 ;
} break ;
default : {
return " %v requires a vector type (Vector2/3/4/2i/3i/4i) " ;
}
}
Vector4 vec = values [ value_index ] ;
String str = " ( " ;
for ( int i = 0 ; i < count ; i + + ) {
double val = vec [ i ] ;
2023-09-05 08:31:29 +02:00
String number_str = String : : num ( Math : : abs ( val ) , min_decimals ) ;
2022-08-11 10:12:27 +02:00
const bool is_finite = Math : : is_finite ( val ) ;
2022-08-24 15:41:31 +02:00
2022-07-31 17:50:02 +02:00
// Pad decimals out.
2022-08-11 10:12:27 +02:00
if ( is_finite ) {
2022-08-24 15:41:31 +02:00
number_str = number_str . pad_decimals ( min_decimals ) ;
}
2022-07-31 17:50:02 +02:00
int initial_len = number_str . length ( ) ;
// Padding. Leave room for sign later if required.
int pad_chars_count = val < 0 ? min_chars - 1 : min_chars ;
2022-08-11 10:12:27 +02:00
String pad_char = ( pad_with_zeros & & is_finite ) ? String ( " 0 " ) : String ( " " ) ; // Never pad NaN or inf with zeros
2022-07-31 17:50:02 +02:00
if ( left_justified ) {
number_str = number_str . rpad ( pad_chars_count , pad_char ) ;
} else {
number_str = number_str . lpad ( pad_chars_count , pad_char ) ;
}
// Add sign if needed.
if ( val < 0 ) {
if ( left_justified ) {
number_str = number_str . insert ( 0 , " - " ) ;
} else {
number_str = number_str . insert ( pad_with_zeros ? 0 : number_str . length ( ) - initial_len , " - " ) ;
}
}
// Add number to combined string
str + = number_str ;
if ( i < count - 1 ) {
str + = " , " ;
}
}
str + = " ) " ;
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
2015-02-01 19:42:36 +01:00
case ' s ' : { // String
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
String str = values [ value_index ] ;
// Padding.
if ( left_justified ) {
str = str . rpad ( min_chars ) ;
} else {
str = str . lpad ( min_chars ) ;
}
2015-01-10 21:44:20 +01:00
2015-02-01 19:42:36 +01:00
formatted + = str ;
2015-01-10 21:44:20 +01:00
+ + value_index ;
in_format = false ;
break ;
2015-02-01 19:42:36 +01:00
}
2015-02-01 21:18:38 +01:00
case ' c ' : {
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
// Convert to character.
String str ;
if ( values [ value_index ] . is_num ( ) ) {
int value = values [ value_index ] ;
if ( value < 0 ) {
2020-07-27 12:43:20 +02:00
return " unsigned integer is lower than minimum " ;
} else if ( value > = 0xd800 & & value < = 0xdfff ) {
return " unsigned integer is invalid Unicode character " ;
} else if ( value > 0x10ffff ) {
return " unsigned integer is greater than maximum " ;
2015-02-01 21:18:38 +01:00
}
str = chr ( values [ value_index ] ) ;
} else if ( values [ value_index ] . get_type ( ) = = Variant : : STRING ) {
str = values [ value_index ] ;
if ( str . length ( ) ! = 1 ) {
2015-02-19 16:45:49 +01:00
return " %c requires number or single-character string " ;
2015-02-01 21:18:38 +01:00
}
} else {
2015-02-19 16:45:49 +01:00
return " %c requires number or single-character string " ;
2015-02-01 21:18:38 +01:00
}
// Padding.
if ( left_justified ) {
str = str . rpad ( min_chars ) ;
} else {
str = str . lpad ( min_chars ) ;
}
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
2015-02-01 19:42:36 +01:00
case ' - ' : { // Left justify
left_justified = true ;
break ;
}
case ' + ' : { // Show + if positive.
show_sign = true ;
break ;
}
2023-04-18 13:33:30 +02:00
case ' u ' : { // Treat as unsigned (for int/hex).
as_unsigned = true ;
break ;
}
2015-02-01 19:42:36 +01:00
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' : {
int n = c - ' 0 ' ;
if ( in_decimals ) {
2015-02-01 21:18:38 +01:00
min_decimals * = 10 ;
min_decimals + = n ;
2015-02-01 19:42:36 +01:00
} else {
if ( c = = ' 0 ' & & min_chars = = 0 ) {
2022-05-01 05:04:52 +02:00
if ( left_justified ) {
WARN_PRINT ( " '0' flag ignored with '-' flag in string format " ) ;
} else {
pad_with_zeros = true ;
}
2015-02-01 19:42:36 +01:00
} else {
min_chars * = 10 ;
min_chars + = n ;
}
}
break ;
}
2022-07-31 17:50:02 +02:00
case ' . ' : { // Float/Vector separator.
2015-02-01 21:18:38 +01:00
if ( in_decimals ) {
2015-02-19 16:45:49 +01:00
return " too many decimal points in format " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
in_decimals = true ;
2015-02-01 21:18:38 +01:00
min_decimals = 0 ; // We want to add the value manually.
2015-02-01 19:42:36 +01:00
break ;
2015-02-01 21:18:38 +01:00
}
2017-09-01 22:33:39 +02:00
case ' * ' : { // Dynamic width, based on value.
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2022-07-31 17:50:02 +02:00
Variant : : Type value_type = values [ value_index ] . get_type ( ) ;
if ( ! values [ value_index ] . is_num ( ) & &
value_type ! = Variant : : VECTOR2 & & value_type ! = Variant : : VECTOR2I & &
value_type ! = Variant : : VECTOR3 & & value_type ! = Variant : : VECTOR3I & &
value_type ! = Variant : : VECTOR4 & & value_type ! = Variant : : VECTOR4I ) {
return " * wants number or vector " ;
2015-02-01 21:18:38 +01:00
}
int size = values [ value_index ] ;
if ( in_decimals ) {
min_decimals = size ;
} else {
min_chars = size ;
}
2015-01-10 21:44:20 +01:00
2015-02-01 21:18:38 +01:00
+ + value_index ;
break ;
}
2014-02-10 02:10:30 +01:00
2015-02-01 21:18:38 +01:00
default : {
2015-02-19 16:45:49 +01:00
return " unsupported format character " ;
2015-02-01 21:18:38 +01:00
}
2015-01-10 21:44:20 +01:00
}
} else { // Not in format string.
switch ( c ) {
case ' % ' :
in_format = true ;
2015-02-01 19:42:36 +01:00
// Back to defaults:
min_chars = 0 ;
2015-02-01 21:18:38 +01:00
min_decimals = 6 ;
2022-05-01 05:04:52 +02:00
pad_with_zeros = false ;
2015-02-01 19:42:36 +01:00
left_justified = false ;
show_sign = false ;
in_decimals = false ;
2015-01-10 21:44:20 +01:00
break ;
default :
formatted + = chr ( c ) ;
}
}
}
2015-02-01 21:18:38 +01:00
if ( in_format ) {
2015-02-19 16:45:49 +01:00
return " incomplete format " ;
2015-02-01 21:18:38 +01:00
}
if ( value_index ! = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not all arguments converted during string formatting " ;
2015-02-01 21:18:38 +01:00
}
2020-11-05 03:01:55 +01:00
if ( error ) {
* error = false ;
}
2015-01-10 21:44:20 +01:00
return formatted ;
}
2016-05-04 03:25:37 +02:00
2023-11-02 18:36:57 +01:00
String String : : quote ( const String & quotechar ) const {
2017-10-28 23:46:20 +02:00
return quotechar + * this + quotechar ;
}
String String : : unquote ( ) const {
if ( ! is_quoted ( ) ) {
return * this ;
}
return substr ( 1 , length ( ) - 2 ) ;
}
2020-10-13 20:59:37 +02:00
Vector < uint8_t > String : : to_ascii_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
CharString charstr = s - > ascii ( ) ;
Vector < uint8_t > retval ;
size_t len = charstr . length ( ) ;
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , charstr . ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
Vector < uint8_t > String : : to_utf8_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
CharString charstr = s - > utf8 ( ) ;
Vector < uint8_t > retval ;
size_t len = charstr . length ( ) ;
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , charstr . ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
Vector < uint8_t > String : : to_utf16_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
Char16String charstr = s - > utf16 ( ) ;
Vector < uint8_t > retval ;
2020-08-05 08:25:28 +02:00
size_t len = charstr . length ( ) * sizeof ( char16_t ) ;
2020-10-13 20:59:37 +02:00
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , ( const void * ) charstr . ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
Vector < uint8_t > String : : to_utf32_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
Vector < uint8_t > retval ;
2020-08-05 08:25:28 +02:00
size_t len = s - > length ( ) * sizeof ( char32_t ) ;
2020-10-13 20:59:37 +02:00
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , ( const void * ) s - > ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
2023-02-13 15:18:12 +01:00
Vector < uint8_t > String : : to_wchar_buffer ( ) const {
# ifdef WINDOWS_ENABLED
return to_utf16_buffer ( ) ;
# else
return to_utf32_buffer ( ) ;
# endif
}
2016-05-17 23:27:15 +02:00
# ifdef TOOLS_ENABLED
2022-03-28 22:34:16 +02:00
/**
* " Tools TRanslate " . Performs string replacement for internationalization
* within the editor . A translation context can optionally be specified to
* disambiguate between identical source strings in translations . When
* placeholders are desired , use ` vformat ( TTR ( " Example: %s " ) , some_string ) ` .
* If a string mentions a quantity ( and may therefore need a dynamic plural form ) ,
* use ` TTRN ( ) ` instead of ` TTR ( ) ` .
*
* NOTE : Only use ` TTR ( ) ` in editor - only code ( typically within the ` editor / ` folder ) .
* For translations that can be supplied by exported projects , use ` RTR ( ) ` instead .
*/
2020-07-16 10:52:06 +02:00
String TTR ( const String & p_text , const String & p_context ) {
2016-05-04 03:25:37 +02:00
if ( TranslationServer : : get_singleton ( ) ) {
2020-07-16 10:52:06 +02:00
return TranslationServer : : get_singleton ( ) - > tool_translate ( p_text , p_context ) ;
2016-05-04 03:25:37 +02:00
}
return p_text ;
}
2016-05-17 23:27:15 +02:00
2022-03-28 22:34:16 +02:00
/**
* " Tools TRanslate for N items " . Performs string replacement for
* internationalization within the editor . A translation context can optionally
* be specified to disambiguate between identical source strings in
* translations . Use ` TTR ( ) ` if the string doesn ' t need dynamic plural form .
* When placeholders are desired , use
* ` vformat ( TTRN ( " %d item " , " %d items " , some_integer ) , some_integer ) ` .
* The placeholder must be present in both strings to avoid run - time warnings in ` vformat ( ) ` .
*
* NOTE : Only use ` TTRN ( ) ` in editor - only code ( typically within the ` editor / ` folder ) .
* For translations that can be supplied by exported projects , use ` RTRN ( ) ` instead .
*/
2020-07-16 10:52:06 +02:00
String TTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context ) {
if ( TranslationServer : : get_singleton ( ) ) {
return TranslationServer : : get_singleton ( ) - > tool_translate_plural ( p_text , p_text_plural , p_n , p_context ) ;
}
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
return p_text ;
}
2020-08-07 13:17:12 +02:00
return p_text_plural ;
2020-07-16 10:52:06 +02:00
}
2022-03-28 22:34:16 +02:00
/**
* " Docs TRanslate " . Used for the editor class reference documentation ,
* handling descriptions extracted from the XML .
* It also replaces ` $ DOCS_URL ` with the actual URL to the documentation ' s branch ,
2021-11-15 10:39:00 +01:00
* to allow dehardcoding it in the XML and doing proper substitutions everywhere .
*/
2020-07-16 10:52:06 +02:00
String DTR ( const String & p_text , const String & p_context ) {
2020-05-28 12:02:12 +02:00
// Comes straight from the XML, so remove indentation and any trailing whitespace.
const String text = p_text . dedent ( ) . strip_edges ( ) ;
2020-03-18 18:34:36 +01:00
if ( TranslationServer : : get_singleton ( ) ) {
2021-11-15 10:39:00 +01:00
return String ( TranslationServer : : get_singleton ( ) - > doc_translate ( text , p_context ) ) . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
2020-03-18 18:34:36 +01:00
}
2021-11-15 10:39:00 +01:00
return text . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
2020-03-18 18:34:36 +01:00
}
2020-07-16 10:52:06 +02:00
2022-03-28 22:34:16 +02:00
/**
* " Docs TRanslate for N items " . Used for the editor class reference documentation
* ( with support for plurals ) , handling descriptions extracted from the XML .
* It also replaces ` $ DOCS_URL ` with the actual URL to the documentation ' s branch ,
* to allow dehardcoding it in the XML and doing proper substitutions everywhere .
*/
2020-07-16 10:52:06 +02:00
String DTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context ) {
const String text = p_text . dedent ( ) . strip_edges ( ) ;
const String text_plural = p_text_plural . dedent ( ) . strip_edges ( ) ;
if ( TranslationServer : : get_singleton ( ) ) {
2021-11-15 10:39:00 +01:00
return String ( TranslationServer : : get_singleton ( ) - > doc_translate_plural ( text , text_plural , p_n , p_context ) ) . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
2020-07-16 10:52:06 +02:00
}
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
2021-11-15 10:39:00 +01:00
return text . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
2020-07-16 10:52:06 +02:00
}
2021-11-15 10:39:00 +01:00
return text_plural . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
2020-07-16 10:52:06 +02:00
}
2016-05-17 23:27:15 +02:00
# endif
2022-03-28 22:34:16 +02:00
/**
* " Run-time TRanslate " . Performs string replacement for internationalization
* within a running project . The translation string must be supplied by the
* project , as Godot does not provide built - in translations for ` RTR ( ) ` strings
* to keep binary size low . A translation context can optionally be specified to
* disambiguate between identical source strings in translations . When
* placeholders are desired , use ` vformat ( RTR ( " Example: %s " ) , some_string ) ` .
* If a string mentions a quantity ( and may therefore need a dynamic plural form ) ,
* use ` RTRN ( ) ` instead of ` RTR ( ) ` .
*
* NOTE : Do not use ` RTR ( ) ` in editor - only code ( typically within the ` editor / `
* folder ) . For editor translations , use ` TTR ( ) ` instead .
*/
2020-07-16 10:52:06 +02:00
String RTR ( const String & p_text , const String & p_context ) {
2016-05-17 23:27:15 +02:00
if ( TranslationServer : : get_singleton ( ) ) {
2020-07-16 10:52:06 +02:00
String rtr = TranslationServer : : get_singleton ( ) - > tool_translate ( p_text , p_context ) ;
2021-12-09 10:42:46 +01:00
if ( rtr . is_empty ( ) | | rtr = = p_text ) {
2020-07-16 10:52:06 +02:00
return TranslationServer : : get_singleton ( ) - > translate ( p_text , p_context ) ;
2016-05-17 23:27:15 +02:00
} else {
return rtr ;
}
}
return p_text ;
}
2020-07-16 10:52:06 +02:00
2022-03-28 22:34:16 +02:00
/**
* " Run-time TRanslate for N items " . Performs string replacement for
* internationalization within a running project . The translation string must be
* supplied by the project , as Godot does not provide built - in translations for
* ` RTRN ( ) ` strings to keep binary size low . A translation context can
* optionally be specified to disambiguate between identical source strings in
* translations . Use ` RTR ( ) ` if the string doesn ' t need dynamic plural form .
* When placeholders are desired , use
* ` vformat ( RTRN ( " %d item " , " %d items " , some_integer ) , some_integer ) ` .
* The placeholder must be present in both strings to avoid run - time warnings in ` vformat ( ) ` .
*
* NOTE : Do not use ` RTRN ( ) ` in editor - only code ( typically within the ` editor / `
* folder ) . For editor translations , use ` TTRN ( ) ` instead .
*/
2020-07-16 10:52:06 +02:00
String RTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context ) {
if ( TranslationServer : : get_singleton ( ) ) {
String rtr = TranslationServer : : get_singleton ( ) - > tool_translate_plural ( p_text , p_text_plural , p_n , p_context ) ;
2021-12-09 10:42:46 +01:00
if ( rtr . is_empty ( ) | | rtr = = p_text | | rtr = = p_text_plural ) {
2020-07-16 10:52:06 +02:00
return TranslationServer : : get_singleton ( ) - > translate_plural ( p_text , p_text_plural , p_n , p_context ) ;
} else {
return rtr ;
}
}
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
return p_text ;
}
2020-08-07 13:17:12 +02:00
return p_text_plural ;
2020-07-16 10:52:06 +02:00
}