2022-03-15 13:29:32 +01:00
/*************************************************************************/
/* ustring.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# ifdef _MSC_VER
# define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
# endif
# include "ustring.h"
# include "core/color.h"
# include "core/crypto/crypto_core.h"
# include "core/math/math_funcs.h"
# include "core/os/memory.h"
# include "core/print_string.h"
# include "core/translation.h"
# include "core/ucaps.h"
# include "core/variant.h"
# include "core/version_generated.gen.h"
# include <wchar.h>
# include <cstdint>
# ifndef NO_USE_STDLIB
# include <stdio.h>
# include <stdlib.h>
# endif
# if defined(MINGW_ENABLED) || defined(_MSC_VER)
# define snprintf _snprintf_s
# endif
# define MAX_DIGITS 6
# define UPPERCASE(m_c) (((m_c) >= 'a' && (m_c) <= 'z') ? ((m_c) - ('a' - 'A')) : (m_c))
# define LOWERCASE(m_c) (((m_c) >= 'A' && (m_c) <= 'Z') ? ((m_c) + ('a' - 'A')) : (m_c))
# define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9')
# define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F'))
const char CharString : : _null = 0 ;
const CharType String : : _null = 0 ;
bool is_symbol ( CharType c ) {
return c ! = ' _ ' & & ( ( c > = ' ! ' & & c < = ' / ' ) | | ( c > = ' : ' & & c < = ' @ ' ) | | ( c > = ' [ ' & & c < = ' ` ' ) | | ( c > = ' { ' & & c < = ' ~ ' ) | | c = = ' \t ' | | c = = ' ' ) ;
}
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 + + ;
}
if ( end < s . length ( ) ) {
end + = 1 ;
}
r_beg = beg ;
r_end = end ;
return true ;
} else {
return false ;
}
}
/** STRING **/
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 ( ) ) ;
}
CharString & CharString : : operator + = ( char p_char ) {
const int lhs_len = length ( ) ;
resize ( lhs_len + 2 ) ;
char * dst = ptrw ( ) ;
dst [ lhs_len ] = p_char ;
dst [ lhs_len + 1 ] = 0 ;
return * this ;
}
const char * CharString : : get_data ( ) const {
if ( size ( ) ) {
return & operator [ ] ( 0 ) ;
} else {
return " " ;
}
}
CharString & CharString : : operator = ( const char * p_cstr ) {
copy_from ( p_cstr ) ;
return * this ;
}
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 ;
}
resize ( len + 1 ) ; // include terminating null char
strcpy ( ptrw ( ) , p_cstr ) ;
}
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
if ( base . get_slice_count ( " : " ) > 2 ) {
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 . empty ( ) ) {
return ERR_INVALID_PARAMETER ;
}
r_host = r_host . to_lower ( ) ;
// Port
if ( base . begins_with ( " : " ) ) {
base = base . substr ( 1 , base . length ( ) - 1 ) ;
if ( ! base . is_valid_integer ( ) ) {
return ERR_INVALID_PARAMETER ;
}
r_port = base . to_int ( ) ;
if ( r_port < 1 | | r_port > 65535 ) {
return ERR_INVALID_PARAMETER ;
}
}
return OK ;
}
void String : : copy_from ( const char * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
const size_t len = strlen ( p_cstr ) ;
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
resize ( len + 1 ) ; // include 0
CharType * dst = this - > ptrw ( ) ;
for ( size_t i = 0 ; i < = len ; i + + ) {
dst [ i ] = p_cstr [ i ] ;
}
}
void String : : copy_from ( const CharType * p_cstr , const int p_clip_to ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
int len = 0 ;
const CharType * 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 != NULL
// p_length > 0
// p_length <= p_char strlen
void String : : copy_from_unchecked ( const CharType * p_char , const int p_length ) {
resize ( p_length + 1 ) ;
CharType * dst = ptrw ( ) ;
memcpy ( dst , p_char , p_length * sizeof ( CharType ) ) ;
dst [ p_length ] = 0 ;
}
void String : : copy_from ( const CharType & p_char ) {
resize ( 2 ) ;
CharType * dst = ptrw ( ) ;
dst [ 0 ] = p_char ;
dst [ 1 ] = 0 ;
}
bool String : : operator = = ( const String & p_str ) const {
if ( length ( ) ! = p_str . length ( ) ) {
return false ;
}
if ( empty ( ) ) {
return true ;
}
int l = length ( ) ;
const CharType * src = c_str ( ) ;
const CharType * dst = p_str . c_str ( ) ;
/* Compare char by char */
for ( int i = 0 ; i < l ; i + + ) {
if ( src [ i ] ! = dst [ i ] ) {
return false ;
}
}
return true ;
}
bool String : : operator ! = ( const String & p_str ) const {
return ! ( * this = = p_str ) ;
}
String String : : operator + ( const String & p_str ) const {
String res = * this ;
res + = p_str ;
return res ;
}
String & String : : operator + = ( const String & p_str ) {
const int lhs_len = length ( ) ;
if ( lhs_len = = 0 ) {
* this = p_str ;
return * this ;
}
const int rhs_len = p_str . length ( ) ;
if ( rhs_len = = 0 ) {
return * this ;
}
resize ( lhs_len + rhs_len + 1 ) ;
const CharType * src = p_str . c_str ( ) ;
CharType * dst = ptrw ( ) + lhs_len ;
memcpy ( dst , src , ( rhs_len + 1 ) * sizeof ( CharType ) ) ;
return * this ;
}
String & String : : operator + = ( const CharType * p_str ) {
* this + = String ( p_str ) ;
return * this ;
}
String & String : : operator + = ( CharType p_char ) {
const int lhs_len = length ( ) ;
resize ( lhs_len + 2 ) ;
CharType * dst = ptrw ( ) ;
dst [ lhs_len ] = p_char ;
dst [ lhs_len + 1 ] = 0 ;
return * this ;
}
String & String : : operator + = ( const char * p_str ) {
if ( ! p_str | | p_str [ 0 ] = = 0 ) {
return * this ;
}
const size_t rhs_len = strlen ( p_str ) ;
const int lhs_len = length ( ) ;
resize ( lhs_len + rhs_len + 1 ) ;
CharType * dst = ptrw ( ) + lhs_len ;
for ( size_t i = 0 ; i < = rhs_len ; i + + ) {
dst [ i ] = p_str [ i ] ;
}
return * this ;
}
void String : : operator = ( const char * p_str ) {
copy_from ( p_str ) ;
}
void String : : operator = ( const CharType * p_str ) {
copy_from ( p_str ) ;
}
bool String : : operator = = ( const StrRange & p_str_range ) const {
int len = p_str_range . len ;
if ( length ( ) ! = len ) {
return false ;
}
if ( empty ( ) ) {
return true ;
}
const CharType * c_str = p_str_range . c_str ;
const CharType * dst = & operator [ ] ( 0 ) ;
/* Compare char by char */
for ( int i = 0 ; i < len ; i + + ) {
if ( c_str [ i ] ! = dst [ i ] ) {
return false ;
}
}
return true ;
}
bool String : : operator = = ( const char * p_str ) const {
int len = 0 ;
const char * aux = p_str ;
while ( * ( aux + + ) ! = 0 ) {
len + + ;
}
if ( length ( ) ! = len ) {
return false ;
}
if ( empty ( ) ) {
return true ;
}
int l = length ( ) ;
const CharType * dst = c_str ( ) ;
/* Compare char by char */
for ( int i = 0 ; i < l ; i + + ) {
if ( p_str [ i ] ! = dst [ i ] ) {
return false ;
}
}
return true ;
}
bool String : : operator = = ( const CharType * p_str ) const {
int len = 0 ;
const CharType * aux = p_str ;
while ( * ( aux + + ) ! = 0 ) {
len + + ;
}
if ( length ( ) ! = len ) {
return false ;
}
if ( empty ( ) ) {
return true ;
}
int l = length ( ) ;
const CharType * dst = c_str ( ) ;
/* Compare char by char */
for ( int i = 0 ; i < l ; i + + ) {
if ( p_str [ i ] ! = dst [ i ] ) {
return false ;
}
}
return true ;
}
bool String : : operator ! = ( const char * p_str ) const {
return ( ! ( * this = = p_str ) ) ;
}
bool String : : operator ! = ( const CharType * p_str ) const {
return ( ! ( * this = = p_str ) ) ;
}
bool String : : operator < ( const CharType * p_str ) const {
if ( empty ( ) & & p_str [ 0 ] = = 0 ) {
return false ;
}
if ( empty ( ) ) {
return true ;
}
return is_str_less ( c_str ( ) , p_str ) ;
}
bool String : : operator < = ( const String & p_str ) const {
return ( * this < p_str ) | | ( * this = = p_str ) ;
}
bool String : : operator < ( const char * p_str ) const {
if ( empty ( ) & & p_str [ 0 ] = = 0 ) {
return false ;
}
if ( empty ( ) ) {
return true ;
}
return is_str_less ( c_str ( ) , p_str ) ;
}
bool String : : operator < ( const String & p_str ) const {
return operator < ( p_str . c_str ( ) ) ;
}
signed char String : : nocasecmp_to ( const String & p_str ) const {
if ( empty ( ) & & p_str . empty ( ) ) {
return 0 ;
}
if ( empty ( ) ) {
return - 1 ;
}
if ( p_str . empty ( ) ) {
return 1 ;
}
const CharType * that_str = p_str . c_str ( ) ;
const CharType * this_str = c_str ( ) ;
while ( true ) {
if ( * that_str = = 0 & & * this_str = = 0 ) {
return 0 ; //we're equal
} else if ( * this_str = = 0 ) {
return - 1 ; //if this is empty, and the other one is not, then we're less.. I think?
} else if ( * that_str = = 0 ) {
return 1 ; //otherwise the other one is smaller..
} else if ( _find_upper ( * this_str ) < _find_upper ( * that_str ) ) { //more than
return - 1 ;
} else if ( _find_upper ( * this_str ) > _find_upper ( * that_str ) ) { //less than
return 1 ;
}
this_str + + ;
that_str + + ;
}
}
signed char String : : casecmp_to ( const String & p_str ) const {
if ( empty ( ) & & p_str . empty ( ) ) {
return 0 ;
}
if ( empty ( ) ) {
return - 1 ;
}
if ( p_str . empty ( ) ) {
return 1 ;
}
const CharType * that_str = p_str . c_str ( ) ;
const CharType * this_str = c_str ( ) ;
while ( true ) {
if ( * that_str = = 0 & & * this_str = = 0 ) {
return 0 ; //we're equal
} else if ( * this_str = = 0 ) {
return - 1 ; //if this is empty, and the other one is not, then we're less.. I think?
} else if ( * that_str = = 0 ) {
return 1 ; //otherwise the other one is smaller..
} else if ( * this_str < * that_str ) { //more than
return - 1 ;
} else if ( * this_str > * that_str ) { //less than
return 1 ;
}
this_str + + ;
that_str + + ;
}
}
signed char String : : naturalnocasecmp_to ( const String & p_str ) const {
const CharType * this_str = c_str ( ) ;
const CharType * that_str = p_str . c_str ( ) ;
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 ) ) {
return - 1 ;
}
// Keep ptrs to start of numerical sequences
const CharType * this_substr = this_str ;
const CharType * that_substr = that_str ;
// Compare lengths of both numerical sequences, ignoring leading zeros
while ( IS_DIGIT ( * this_str ) ) {
this_str + + ;
}
while ( IS_DIGIT ( * that_str ) ) {
that_str + + ;
}
while ( * this_substr = = ' 0 ' ) {
this_substr + + ;
}
while ( * that_substr = = ' 0 ' ) {
that_substr + + ;
}
int this_len = this_str - this_substr ;
int that_len = 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 ! = this_str & & that_substr ! = that_str ) {
if ( * this_substr < * that_substr ) {
return - 1 ;
} else if ( * this_substr > * that_substr ) {
return 1 ;
}
this_substr + + ;
that_substr + + ;
}
} else if ( IS_DIGIT ( * that_str ) ) {
return 1 ;
} else {
if ( _find_upper ( * this_str ) < _find_upper ( * that_str ) ) { //more than
return - 1 ;
} else if ( _find_upper ( * this_str ) > _find_upper ( * that_str ) ) { //less than
return 1 ;
}
this_str + + ;
that_str + + ;
}
}
if ( * that_str ) {
return - 1 ;
}
}
return 0 ;
}
void String : : erase ( int p_pos , int p_chars ) {
* this = left ( p_pos ) + substr ( p_pos + p_chars , length ( ) - ( ( p_pos + p_chars ) ) ) ;
}
String String : : capitalize ( ) const {
String aux = this - > camelcase_to_underscore ( true ) . replace ( " _ " , " " ) . strip_edges ( ) ;
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 : : camelcase_to_underscore ( bool lowercase ) const {
const CharType * cstr = c_str ( ) ;
String new_string ;
const char A = ' A ' , Z = ' Z ' ;
const char a = ' a ' , z = ' z ' ;
int start_index = 0 ;
for ( int i = 1 ; i < this - > size ( ) ; i + + ) {
bool is_upper = cstr [ i ] > = A & & cstr [ i ] < = Z ;
bool is_number = cstr [ i ] > = ' 0 ' & & cstr [ i ] < = ' 9 ' ;
bool are_next_2_lower = false ;
bool is_next_lower = false ;
bool is_next_number = false ;
bool was_precedent_upper = cstr [ i - 1 ] > = A & & cstr [ i - 1 ] < = Z ;
bool was_precedent_number = cstr [ i - 1 ] > = ' 0 ' & & cstr [ i - 1 ] < = ' 9 ' ;
if ( i + 2 < this - > size ( ) ) {
are_next_2_lower = cstr [ i + 1 ] > = a & & cstr [ i + 1 ] < = z & & cstr [ i + 2 ] > = a & & cstr [ i + 2 ] < = z ;
}
if ( i + 1 < this - > size ( ) ) {
is_next_lower = cstr [ i + 1 ] > = a & & cstr [ i + 1 ] < = z ;
is_next_number = cstr [ i + 1 ] > = ' 0 ' & & cstr [ i + 1 ] < = ' 9 ' ;
}
const bool cond_a = is_upper & & ! was_precedent_upper & & ! was_precedent_number ;
const bool cond_b = was_precedent_upper & & is_upper & & are_next_2_lower ;
const bool cond_c = is_number & & ! was_precedent_number ;
const bool can_break_number_letter = is_number & & ! was_precedent_number & & is_next_lower ;
const bool can_break_letter_number = ! is_number & & was_precedent_number & & ( is_next_lower | | is_next_number ) ;
bool should_split = cond_a | | cond_b | | cond_c | | can_break_number_letter | | can_break_letter_number ;
if ( should_split ) {
new_string + = this - > substr ( start_index , i - start_index ) + " _ " ;
start_index = i ;
}
}
new_string + = this - > substr ( start_index , this - > size ( ) - start_index ) ;
return lowercase ? new_string . to_lower ( ) : new_string ;
}
int String : : get_slice_count ( String p_splitter ) const {
if ( empty ( ) ) {
return 0 ;
}
if ( p_splitter . empty ( ) ) {
return 0 ;
}
int pos = 0 ;
int slices = 1 ;
while ( ( pos = find ( p_splitter , pos ) ) > = 0 ) {
slices + + ;
pos + = p_splitter . length ( ) ;
}
return slices ;
}
String String : : get_slice ( String p_splitter , int p_slice ) const {
if ( empty ( ) | | p_splitter . empty ( ) ) {
return " " ;
}
int pos = 0 ;
int prev_pos = 0 ;
//int slices=1;
if ( p_slice < 0 ) {
return " " ;
}
if ( find ( p_splitter ) = = - 1 ) {
return * this ;
}
int i = 0 ;
while ( true ) {
pos = find ( p_splitter , pos ) ;
if ( pos = = - 1 ) {
pos = length ( ) ; //reached end
}
int from = prev_pos ;
//int to=pos;
if ( p_slice = = i ) {
return substr ( from , pos - from ) ;
}
if ( pos = = length ( ) ) { //reached end and no find
break ;
}
pos + = p_splitter . length ( ) ;
prev_pos = pos ;
i + + ;
}
return " " ; //no find!
}
String String : : get_slicec ( CharType p_splitter , int p_slice ) const {
if ( empty ( ) ) {
return String ( ) ;
}
if ( p_slice < 0 ) {
return String ( ) ;
}
const CharType * c = this - > ptr ( ) ;
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 ) ;
} else if ( c [ i ] = = 0 ) {
return String ( ) ;
} else {
count + + ;
prev = i + 1 ;
}
}
i + + ;
}
}
Vector < String > String : : split_spaces ( ) const {
Vector < String > ret ;
int from = 0 ;
int i = 0 ;
int len = length ( ) ;
if ( len = = 0 ) {
return ret ;
}
bool inside = false ;
while ( true ) {
bool empty = operator [ ] ( i ) < 33 ;
if ( i = = 0 ) {
inside = ! empty ;
}
if ( ! empty & & ! inside ) {
inside = true ;
from = i ;
}
if ( empty & & inside ) {
ret . push_back ( substr ( from , i - from ) ) ;
inside = false ;
}
if ( i = = len ) {
break ;
}
i + + ;
}
return ret ;
}
Vector < String > String : : split ( const String & p_splitter , bool p_allow_empty , int p_maxsplit ) const {
Vector < String > ret ;
int from = 0 ;
int len = length ( ) ;
while ( true ) {
int end = find ( p_splitter , from ) ;
if ( end < 0 ) {
end = len ;
}
if ( p_allow_empty | | ( end > from ) ) {
if ( p_maxsplit < = 0 ) {
ret . push_back ( substr ( from , end - from ) ) ;
} else {
// 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 ) ) ;
}
}
if ( end = = len ) {
break ;
}
from = end + p_splitter . length ( ) ;
}
return ret ;
}
Vector < String > String : : rsplit ( const String & p_splitter , bool p_allow_empty , int p_maxsplit ) const {
Vector < String > ret ;
const int len = length ( ) ;
int remaining_len = len ;
while ( true ) {
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 ) ) ;
}
break ;
}
int left_edge = rfind ( p_splitter , remaining_len - p_splitter . length ( ) ) ;
if ( left_edge < 0 ) {
// no more splitters, we're done
ret . push_back ( substr ( 0 , remaining_len ) ) ;
break ;
}
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 ) ) ;
}
remaining_len = left_edge ;
}
ret . invert ( ) ;
return ret ;
}
Vector < float > String : : split_floats ( const String & p_splitter , bool p_allow_empty ) const {
Vector < float > ret ;
int from = 0 ;
int len = length ( ) ;
while ( true ) {
int end = find ( p_splitter , from ) ;
if ( end < 0 ) {
end = len ;
}
if ( p_allow_empty | | ( end > from ) ) {
ret . push_back ( String : : to_double ( & c_str ( ) [ from ] ) ) ;
}
if ( end = = len ) {
break ;
}
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 ( ) ;
}
if ( p_allow_empty | | ( end > from ) ) {
ret . push_back ( String : : to_double ( & c_str ( ) [ from ] ) ) ;
}
if ( end = = len ) {
break ;
}
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 ) ;
if ( end < 0 ) {
end = len ;
}
if ( p_allow_empty | | ( end > from ) ) {
ret . push_back ( String : : to_int ( & c_str ( ) [ from ] , end - from ) ) ;
}
if ( end = = len ) {
break ;
}
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 ( ) ;
}
if ( p_allow_empty | | ( end > from ) ) {
ret . push_back ( String : : to_int ( & c_str ( ) [ from ] , end - from ) ) ;
}
if ( end = = len ) {
break ;
}
from = end + spl_len ;
}
return ret ;
}
String String : : join ( Vector < String > parts ) {
String ret ;
for ( int i = 0 ; i < parts . size ( ) ; + + i ) {
if ( i > 0 ) {
ret + = * this ;
}
ret + = parts [ i ] ;
}
return ret ;
}
CharType String : : char_uppercase ( CharType p_char ) {
return _find_upper ( p_char ) ;
}
CharType String : : char_lowercase ( CharType p_char ) {
return _find_lower ( p_char ) ;
}
String String : : to_upper ( ) const {
String upper = * this ;
for ( int i = 0 ; i < upper . size ( ) ; i + + ) {
const CharType s = upper [ i ] ;
const CharType t = _find_upper ( s ) ;
if ( s ! = t ) { // avoid copy on write
upper [ i ] = t ;
}
}
return upper ;
}
String String : : to_lower ( ) const {
String lower = * this ;
for ( int i = 0 ; i < lower . size ( ) ; i + + ) {
const CharType s = lower [ i ] ;
const CharType t = _find_lower ( s ) ;
if ( s ! = t ) { // avoid copy on write
lower [ i ] = t ;
}
}
return lower ;
}
const CharType * String : : c_str ( ) const {
static const CharType zero = 0 ;
return size ( ) ? & operator [ ] ( 0 ) : & zero ;
}
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 ;
}
2022-06-26 03:38:28 +02:00
String String : : bool_num ( bool p_val ) {
if ( p_val ) {
return " 1 " ;
} else {
return " 0 " ;
}
}
String String : : bool_str ( bool p_val ) {
if ( p_val ) {
return " true " ;
} else {
return " false " ;
}
}
2022-03-15 13:29:32 +01:00
String String : : chr ( CharType p_char ) {
CharType c [ 2 ] = { p_char , 0 } ;
return String ( c ) ;
}
String String : : num ( double p_num , int p_decimals ) {
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
# ifndef NO_USE_STDLIB
if ( p_decimals > 16 ) {
p_decimals = 16 ;
}
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 ;
}
char buf [ 256 ] ;
# if defined(__GNUC__) || defined(_MSC_VER)
snprintf ( buf , 256 , fmt , p_num ) ;
# else
sprintf ( buf , fmt , p_num ) ;
# endif
buf [ 255 ] = 0 ;
//destroy trailing zeroes
{
bool period = false ;
int z = 0 ;
while ( buf [ z ] ) {
if ( buf [ z ] = = ' . ' ) {
period = true ;
}
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 ;
# else
String s ;
String sd ;
/* integer part */
bool neg = p_num < 0 ;
p_num = ABS ( p_num ) ;
int intn = ( int ) p_num ;
/* decimal part */
if ( p_decimals > 0 | | ( p_decimals = = - 1 & & ( int ) p_num ! = p_num ) ) {
double dec = p_num - ( float ) ( ( int ) p_num ) ;
int digit = 0 ;
if ( p_decimals > MAX_DIGITS )
p_decimals = MAX_DIGITS ;
int dec_int = 0 ;
int dec_max = 0 ;
while ( true ) {
dec * = 10.0 ;
dec_int = dec_int * 10 + ( int ) dec % 10 ;
dec_max = dec_max * 10 + 9 ;
digit + + ;
if ( p_decimals = = - 1 ) {
if ( digit = = MAX_DIGITS ) //no point in going to infinite
break ;
if ( ( dec - ( float ) ( ( int ) dec ) ) < 1e-6 )
break ;
}
if ( digit = = p_decimals )
break ;
}
dec * = 10 ;
int last = ( int ) dec % 10 ;
if ( last > 5 ) {
if ( dec_int = = dec_max ) {
dec_int = 0 ;
intn + + ;
} else {
dec_int + + ;
}
}
String decimal ;
for ( int i = 0 ; i < digit ; i + + ) {
char num [ 2 ] = { 0 , 0 } ;
num [ 0 ] = ' 0 ' + dec_int % 10 ;
decimal = num + decimal ;
dec_int / = 10 ;
}
sd = ' . ' + decimal ;
}
if ( intn = = 0 )
s = " 0 " ;
else {
while ( intn ) {
CharType num = ' 0 ' + ( intn % 10 ) ;
intn / = 10 ;
s = num + s ;
}
}
s = s + sd ;
if ( neg )
s = " - " + s ;
return s ;
# endif
}
String String : : num_int64 ( int64_t p_num , int base , bool capitalize_hex ) {
bool sign = p_num < 0 ;
int64_t n = p_num ;
int chars = 0 ;
do {
n / = base ;
chars + + ;
} while ( n ) ;
if ( sign ) {
chars + + ;
}
String s ;
s . resize ( chars + 1 ) ;
CharType * c = s . ptrw ( ) ;
c [ chars ] = 0 ;
n = p_num ;
do {
int mod = ABS ( n % base ) ;
if ( mod > = 10 ) {
char a = ( capitalize_hex ? ' A ' : ' a ' ) ;
c [ - - chars ] = a + ( mod - 10 ) ;
} else {
c [ - - chars ] = ' 0 ' + mod ;
}
n / = base ;
} while ( n ) ;
if ( sign ) {
c [ 0 ] = ' - ' ;
}
return s ;
}
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 ) ;
CharType * c = s . ptrw ( ) ;
c [ chars ] = 0 ;
n = p_num ;
do {
int mod = n % base ;
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 ;
}
String String : : num_real ( double p_num ) {
String s ;
String sd ;
/* integer part */
bool neg = p_num < 0 ;
p_num = ABS ( p_num ) ;
int intn = ( int ) p_num ;
/* decimal part */
if ( ( int ) p_num ! = p_num ) {
double dec = p_num - ( float ) ( ( int ) p_num ) ;
int digit = 0 ;
int decimals = MAX_DIGITS ;
int dec_int = 0 ;
int dec_max = 0 ;
while ( true ) {
dec * = 10.0 ;
dec_int = dec_int * 10 + ( int ) dec % 10 ;
dec_max = dec_max * 10 + 9 ;
digit + + ;
if ( ( dec - ( float ) ( ( int ) dec ) ) < 1e-6 ) {
break ;
}
if ( digit = = decimals ) {
break ;
}
}
dec * = 10 ;
int last = ( int ) dec % 10 ;
if ( last > 5 ) {
if ( dec_int = = dec_max ) {
dec_int = 0 ;
intn + + ;
} else {
dec_int + + ;
}
}
String decimal ;
for ( int i = 0 ; i < digit ; i + + ) {
char num [ 2 ] = { 0 , 0 } ;
num [ 0 ] = ' 0 ' + dec_int % 10 ;
decimal = num + decimal ;
dec_int / = 10 ;
}
sd = ' . ' + decimal ;
} else {
sd = " .0 " ;
}
if ( intn = = 0 ) {
s = " 0 " ;
} else {
while ( intn ) {
CharType num = ' 0 ' + ( intn % 10 ) ;
intn / = 10 ;
s = num + s ;
}
}
s = s + sd ;
if ( neg ) {
s = " - " + s ;
}
return s ;
}
String String : : num_scientific ( double p_num ) {
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
# ifndef NO_USE_STDLIB
char buf [ 256 ] ;
# if defined(__GNUC__) || defined(_MSC_VER)
# if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
// MinGW requires _set_output_format() to conform to C99 output for printf
unsigned int old_exponent_format = _set_output_format ( _TWO_DIGIT_EXPONENT ) ;
# endif
snprintf ( buf , 256 , " %lg " , p_num ) ;
# if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
_set_output_format ( old_exponent_format ) ;
# endif
# else
sprintf ( buf , " %.16lg " , p_num ) ;
# endif
buf [ 255 ] = 0 ;
return buf ;
# else
return String : : num ( p_num ) ;
# endif
}
CharString String : : ascii ( bool p_allow_extended ) const {
if ( ! length ( ) ) {
return CharString ( ) ;
}
CharString cs ;
cs . resize ( size ( ) ) ;
for ( int i = 0 ; i < size ( ) ; i + + ) {
cs [ i ] = operator [ ] ( i ) ;
}
return cs ;
}
String String : : utf8 ( const char * p_utf8 , int p_len ) {
String ret ;
ret . parse_utf8 ( p_utf8 , p_len ) ;
return ret ;
} ;
bool String : : parse_utf8 ( const char * p_utf8 , int p_len ) {
# define _UNICERROR(m_err) print_line("Unicode error: " + String(m_err));
if ( ! p_utf8 ) {
return true ;
}
String aux ;
int cstr_size = 0 ;
int str_size = 0 ;
/* HANDLE BOM (Byte Order Mark) */
if ( p_len < 0 | | p_len > = 3 ) {
bool has_bom = uint8_t ( p_utf8 [ 0 ] ) = = 0xEF & & uint8_t ( p_utf8 [ 1 ] ) = = 0xBB & & uint8_t ( p_utf8 [ 2 ] ) = = 0xBF ;
if ( has_bom ) {
//just skip it
if ( p_len > = 0 ) {
p_len - = 3 ;
}
p_utf8 + = 3 ;
}
}
{
const char * ptrtmp = p_utf8 ;
const char * ptrtmp_limit = & p_utf8 [ p_len ] ;
int skip = 0 ;
while ( ptrtmp ! = ptrtmp_limit & & * ptrtmp ) {
if ( skip = = 0 ) {
uint8_t c = * ptrtmp > = 0 ? * ptrtmp : uint8_t ( 256 + * ptrtmp ) ;
/* Determine the number of characters in sequence */
if ( ( c & 0x80 ) = = 0 ) {
skip = 0 ;
} else if ( ( c & 0xE0 ) = = 0xC0 ) {
skip = 1 ;
} else if ( ( c & 0xF0 ) = = 0xE0 ) {
skip = 2 ;
} else if ( ( c & 0xF8 ) = = 0xF0 ) {
skip = 3 ;
if ( sizeof ( wchar_t ) = = 2 ) {
str_size + + ; // encode as surrogate pair.
}
} else if ( ( c & 0xFC ) = = 0xF8 ) {
skip = 4 ;
// invalid character, too long to encode as surrogates.
} else if ( ( c & 0xFE ) = = 0xFC ) {
skip = 5 ;
// invalid character, too long to encode as surrogates.
} else {
_UNICERROR ( " invalid skip " ) ;
return true ; //invalid utf8
}
if ( skip = = 1 & & ( c & 0x1E ) = = 0 ) {
//printf("overlong rejected\n");
_UNICERROR ( " overlong rejected " ) ;
return true ; //reject overlong
}
str_size + + ;
} else {
- - skip ;
}
cstr_size + + ;
ptrtmp + + ;
}
if ( skip ) {
_UNICERROR ( " no space left " ) ;
return true ; //not enough spac
}
}
if ( str_size = = 0 ) {
clear ( ) ;
return false ;
}
resize ( str_size + 1 ) ;
CharType * dst = ptrw ( ) ;
dst [ str_size ] = 0 ;
while ( cstr_size ) {
int len = 0 ;
/* Determine the number of characters in sequence */
if ( ( * p_utf8 & 0x80 ) = = 0 ) {
len = 1 ;
} else if ( ( * p_utf8 & 0xE0 ) = = 0xC0 ) {
len = 2 ;
} else if ( ( * p_utf8 & 0xF0 ) = = 0xE0 ) {
len = 3 ;
} else if ( ( * p_utf8 & 0xF8 ) = = 0xF0 ) {
len = 4 ;
} else if ( ( * p_utf8 & 0xFC ) = = 0xF8 ) {
len = 5 ;
} else if ( ( * p_utf8 & 0xFE ) = = 0xFC ) {
len = 6 ;
} else {
_UNICERROR ( " invalid len " ) ;
return true ; //invalid UTF8
}
if ( len > cstr_size ) {
_UNICERROR ( " no space left " ) ;
return true ; //not enough space
}
if ( len = = 2 & & ( * p_utf8 & 0x1E ) = = 0 ) {
//printf("overlong rejected\n");
_UNICERROR ( " no space left " ) ;
return true ; //reject overlong
}
/* Convert the first character */
uint32_t unichar = 0 ;
if ( len = = 1 ) {
unichar = * p_utf8 ;
} else {
unichar = ( 0xFF > > ( len + 1 ) ) & * p_utf8 ;
for ( int i = 1 ; i < len ; i + + ) {
if ( ( p_utf8 [ i ] & 0xC0 ) ! = 0x80 ) {
_UNICERROR ( " invalid utf8 " ) ;
return true ; //invalid utf8
}
if ( unichar = = 0 & & i = = 2 & & ( ( p_utf8 [ i ] & 0x7F ) > > ( 7 - len ) ) = = 0 ) {
_UNICERROR ( " invalid utf8 overlong " ) ;
return true ; //no overlong
}
unichar = ( unichar < < 6 ) | ( p_utf8 [ i ] & 0x3F ) ;
}
}
if ( sizeof ( wchar_t ) = = 2 & & unichar > 0x10FFFF ) {
unichar = ' ' ; // invalid character, too long to encode as surrogates.
} else if ( sizeof ( wchar_t ) = = 2 & & unichar > 0xFFFF ) {
* ( dst + + ) = uint32_t ( ( unichar > > 10 ) + 0xD7C0 ) ; // lead surrogate.
* ( dst + + ) = uint32_t ( ( unichar & 0x3FF ) | 0xDC00 ) ; // trail surrogate.
} else {
* ( dst + + ) = unichar ;
}
cstr_size - = len ;
p_utf8 + = len ;
}
return false ;
}
CharString String : : utf8 ( ) const {
int l = length ( ) ;
if ( ! l ) {
return CharString ( ) ;
}
const CharType * d = & operator [ ] ( 0 ) ;
int fl = 0 ;
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
if ( ( c & 0xfffffc00 ) = = 0xd800 ) { // decode surrogate pair.
if ( ( i < l - 1 ) & & ( d [ i + 1 ] & 0xfffffc00 ) = = 0xdc00 ) {
c = ( c < < 10UL ) + d [ i + 1 ] - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ;
i + + ; // skip trail surrogate.
} else {
fl + = 1 ;
continue ;
}
} else if ( ( c & 0xfffffc00 ) = = 0xdc00 ) {
fl + = 1 ;
continue ;
}
if ( c < = 0x7f ) { // 7 bits.
fl + = 1 ;
} else if ( c < = 0x7ff ) { // 11 bits
fl + = 2 ;
} else if ( c < = 0xffff ) { // 16 bits
fl + = 3 ;
} else if ( c < = 0x001fffff ) { // 21 bits
fl + = 4 ;
} else if ( c < = 0x03ffffff ) { // 26 bits
fl + = 5 ;
} else if ( c < = 0x7fffffff ) { // 31 bits
fl + = 6 ;
}
}
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 ] ;
if ( ( c & 0xfffffc00 ) = = 0xd800 ) { // decode surrogate pair.
if ( ( i < l - 1 ) & & ( d [ i + 1 ] & 0xfffffc00 ) = = 0xdc00 ) {
c = ( c < < 10UL ) + d [ i + 1 ] - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ;
i + + ; // skip trail surrogate.
} else {
APPEND_CHAR ( ' ' ) ;
continue ;
}
} else if ( ( c & 0xfffffc00 ) = = 0xdc00 ) {
APPEND_CHAR ( ' ' ) ;
continue ;
}
if ( c < = 0x7f ) { // 7 bits.
APPEND_CHAR ( c ) ;
} else if ( c < = 0x7ff ) { // 11 bits
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.
} else if ( c < = 0x001fffff ) { // 21 bits
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.
} 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.
}
}
# undef APPEND_CHAR
* cdst = 0 ; //trailing zero
return utf8s ;
}
String : : String ( const char * p_str ) {
copy_from ( p_str ) ;
}
String : : String ( const CharType * p_str , int p_clip_to_len ) {
copy_from ( p_str , p_clip_to_len ) ;
}
String : : String ( const StrRange & p_range ) {
if ( ! p_range . c_str ) {
return ;
}
copy_from ( p_range . c_str , p_range . len ) ;
}
int String : : hex_to_int ( bool p_with_prefix ) const {
int len = length ( ) ;
if ( len = = 0 | | ( p_with_prefix & & len < 3 ) ) {
return 0 ;
}
const CharType * s = ptr ( ) ;
int sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
if ( sign < 0 ) {
s + + ;
}
if ( p_with_prefix ) {
if ( s [ 0 ] ! = ' 0 ' | | s [ 1 ] ! = ' x ' ) {
return 0 ;
}
s + = 2 ;
}
int hex = 0 ;
while ( * s ) {
CharType c = LOWERCASE ( * s ) ;
int n ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
n = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
n = ( c - ' a ' ) + 10 ;
} else {
return 0 ;
}
// Check for overflow/underflow, with special case to ensure INT32_MIN does not result in error
bool overflow = ( ( hex > INT32_MAX / 16 ) & & ( sign = = 1 | | ( sign = = - 1 & & hex ! = ( INT32_MAX > > 4 ) + 1 ) ) ) | | ( sign = = - 1 & & hex = = ( INT32_MAX > > 4 ) + 1 & & c > ' 0 ' ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT32_MAX : INT32_MIN , " Cannot represent " + * this + " as integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
hex * = 16 ;
hex + = n ;
s + + ;
}
return hex * sign ;
}
int64_t String : : hex_to_int64 ( bool p_with_prefix ) const {
if ( p_with_prefix & & length ( ) < 3 ) {
return 0 ;
}
const CharType * s = ptr ( ) ;
int64_t sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
if ( sign < 0 ) {
s + + ;
}
if ( p_with_prefix ) {
if ( s [ 0 ] ! = ' 0 ' | | s [ 1 ] ! = ' x ' ) {
return 0 ;
}
s + = 2 ;
}
int64_t hex = 0 ;
while ( * s ) {
CharType c = LOWERCASE ( * s ) ;
int64_t n ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
n = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
n = ( c - ' a ' ) + 10 ;
} else {
return 0 ;
}
bool overflow = ( ( hex > INT64_MAX / 16 ) & & ( sign = = 1 | | ( sign = = - 1 & & hex ! = ( INT64_MAX > > 4 ) + 1 ) ) ) | | ( sign = = - 1 & & hex = = ( INT64_MAX > > 4 ) + 1 & & c > ' 0 ' ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + * this + " as 64-bit integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
hex * = 16 ;
hex + = n ;
s + + ;
}
return hex * sign ;
}
int64_t String : : bin_to_int64 ( bool p_with_prefix ) const {
int len = length ( ) ;
if ( len = = 0 | | ( p_with_prefix & & len < 3 ) ) {
return 0 ;
}
const CharType * s = ptr ( ) ;
int64_t sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
if ( sign < 0 ) {
s + + ;
}
if ( p_with_prefix ) {
if ( s [ 0 ] ! = ' 0 ' | | s [ 1 ] ! = ' b ' ) {
return 0 ;
}
s + = 2 ;
}
int64_t binary = 0 ;
while ( * s ) {
CharType c = LOWERCASE ( * s ) ;
int64_t n ;
if ( c = = ' 0 ' | | c = = ' 1 ' ) {
n = c - ' 0 ' ;
} else {
return 0 ;
}
// 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 ' ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + * this + " as 64-bit integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
binary * = 2 ;
binary + = n ;
s + + ;
}
return binary * sign ;
}
int String : : to_int ( ) const {
if ( length ( ) = = 0 ) {
return 0 ;
}
int to = ( find ( " . " ) > = 0 ) ? find ( " . " ) : length ( ) ;
int integer = 0 ;
int sign = 1 ;
for ( int i = 0 ; i < to ; i + + ) {
CharType c = operator [ ] ( i ) ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
bool overflow = ( integer > INT32_MAX / 10 ) | | ( integer = = INT32_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT32_MAX : INT32_MIN , " Cannot represent " + * this + " as integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
integer * = 10 ;
integer + = c - ' 0 ' ;
} else if ( integer = = 0 & & c = = ' - ' ) {
sign = - sign ;
}
}
return integer * sign ;
}
2022-06-26 03:38:28 +02:00
bool String : : to_bool ( ) const {
if ( length ( ) = = 0 ) {
return false ;
}
if ( is_numeric ( ) ) {
return to_int ( ) ! = 0 ;
}
return to_lower ( ) = = " true " ;
}
uint32_t String : : to_uint ( ) const {
if ( is_numeric ( ) ) {
return static_cast < uint32_t > ( to_int ( ) ) ;
}
return 0 ;
}
2022-03-15 13:29:32 +01:00
int64_t String : : to_int64 ( ) const {
if ( length ( ) = = 0 ) {
return 0 ;
}
int to = ( find ( " . " ) > = 0 ) ? find ( " . " ) : length ( ) ;
int64_t integer = 0 ;
int64_t sign = 1 ;
for ( int i = 0 ; i < to ; i + + ) {
CharType c = operator [ ] ( i ) ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
bool overflow = ( integer > INT64_MAX / 10 ) | | ( integer = = INT64_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + * this + " as 64-bit integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
integer * = 10 ;
integer + = c - ' 0 ' ;
} else if ( integer = = 0 & & c = = ' - ' ) {
sign = - sign ;
}
}
return integer * sign ;
}
int String : : to_int ( const char * 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 + + ;
}
}
int integer = 0 ;
int sign = 1 ;
for ( int i = 0 ; i < to ; i + + ) {
char c = p_str [ i ] ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
bool overflow = ( integer > INT32_MAX / 10 ) | | ( integer = = INT32_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT32_MAX : INT32_MIN , " Cannot represent " + String ( p_str ) . substr ( 0 , to ) + " as integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
integer * = 10 ;
integer + = c - ' 0 ' ;
} else if ( c = = ' - ' & & integer = = 0 ) {
sign = - sign ;
} else if ( c ! = ' ' ) {
break ;
}
}
return integer * sign ;
}
bool String : : is_numeric ( ) const {
if ( length ( ) = = 0 ) {
return false ;
} ;
int s = 0 ;
if ( operator [ ] ( 0 ) = = ' - ' ) {
+ + s ;
}
bool dot = false ;
for ( int i = s ; i < length ( ) ; i + + ) {
CharType c = operator [ ] ( i ) ;
if ( c = = ' . ' ) {
if ( dot ) {
return false ;
} ;
dot = true ;
}
if ( c < ' 0 ' | | c > ' 9 ' ) {
return false ;
} ;
} ;
return true ; // TODO: Use the parser below for this instead
} ;
2022-06-26 03:38:28 +02:00
bool String : : is_zero ( ) const {
int size = length ( ) ;
if ( size = = 0 ) {
return false ;
}
int starti = 0 ;
if ( operator [ ] ( 0 ) = = ' - ' ) {
starti + = 1 ;
}
bool had_dot = false ;
for ( int i = starti ; i < size ; + + i ) {
CharType c = operator [ ] ( i ) ;
if ( c = = ' . ' ) {
if ( ! had_dot ) {
had_dot = true ;
continue ;
} else {
return false ;
}
}
if ( c ! = ' 0 ' ) {
return false ;
}
}
return true ;
}
2022-03-15 13:29:32 +01:00
template < class C >
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. ,
1.0e4 ,
1.0e8 ,
1.0e16 ,
1.0e32 ,
1.0e64 ,
1.0e128 ,
1.0e256
} ;
bool sign , expSign = false ;
double fraction , dblExp ;
const double * d ;
const C * p ;
int c ;
/* 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 ;
/*
* Strip off leading blanks and check for a sign .
*/
p = string ;
while ( * p = = ' ' | | * p = = ' \t ' | | * p = = ' \n ' ) {
p + = 1 ;
}
if ( * p = = ' - ' ) {
sign = true ;
p + = 1 ;
} else {
if ( * p = = ' + ' ) {
p + = 1 ;
}
sign = false ;
}
/*
* Count the number of digits in the mantissa ( including the decimal
* point ) , and also locate the decimal point .
*/
decPt = - 1 ;
for ( mantSize = 0 ; ; mantSize + = 1 ) {
c = * p ;
if ( ! IS_DIGIT ( c ) ) {
if ( ( c ! = ' . ' ) | | ( decPt > = 0 ) ) {
break ;
}
decPt = mantSize ;
}
p + = 1 ;
}
/*
* 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 .
*/
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 ;
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 ' ) ;
}
fraction = ( 1.0e9 * frac1 ) + frac2 ;
}
/*
* Skim off the exponent .
*/
p = pExp ;
if ( ( * p = = ' E ' ) | | ( * p = = ' e ' ) ) {
p + = 1 ;
if ( * p = = ' - ' ) {
expSign = true ;
p + = 1 ;
} else {
if ( * p = = ' + ' ) {
p + = 1 ;
}
expSign = false ;
}
if ( ! IS_DIGIT ( CharType ( * p ) ) ) {
p = pExp ;
goto done ;
}
while ( IS_DIGIT ( CharType ( * p ) ) ) {
exp = exp * 10 + ( * p - ' 0 ' ) ;
p + = 1 ;
}
}
if ( expSign ) {
exp = fracExp - exp ;
} else {
exp = fracExp + exp ;
}
/*
* 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 .
*/
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 ;
}
}
if ( expSign ) {
fraction / = dblExp ;
} else {
fraction * = dblExp ;
}
done :
if ( endPtr ! = nullptr ) {
* 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
double String : : to_double ( const char * p_str ) {
# ifndef NO_USE_STDLIB
return built_in_strtod < char > ( p_str ) ;
# else
return built_in_strtod < char > ( p_str ) ;
# endif
}
float String : : to_float ( ) const {
return to_double ( ) ;
}
double String : : to_double ( const CharType * p_str , const CharType * * r_end ) {
return built_in_strtod < CharType > ( p_str , ( CharType * * ) r_end ) ;
}
int64_t String : : to_int ( const CharType * p_str , int p_len ) {
if ( p_len = = 0 | | ! p_str [ 0 ] ) {
return 0 ;
}
///@todo make more exact so saving and loading does not lose precision
int64_t integer = 0 ;
int64_t sign = 1 ;
int reading = READING_SIGN ;
const CharType * str = p_str ;
const CharType * limit = & p_str [ p_len ] ;
while ( * str & & reading ! = READING_DONE & & str ! = limit ) {
CharType c = * ( str + + ) ;
switch ( reading ) {
case READING_SIGN : {
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
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 ;
}
}
case READING_INT : {
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
if ( integer > INT64_MAX / 10 ) {
String number ( " " ) ;
str = p_str ;
while ( * str & & str ! = limit ) {
number + = * ( str + + ) ;
}
ERR_FAIL_V_MSG ( sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + number + " as integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
}
integer * = 10 ;
integer + = c - ' 0 ' ;
} else {
reading = READING_DONE ;
}
} break ;
}
}
return sign * integer ;
}
double String : : to_double ( ) const {
if ( empty ( ) ) {
return 0 ;
}
# ifndef NO_USE_STDLIB
return built_in_strtod < CharType > ( c_str ( ) ) ;
//return wcstod(c_str(),NULL); DOES NOT WORK ON ANDROID :(
# else
return built_in_strtod < CharType > ( c_str ( ) ) ;
# endif
}
bool operator = = ( const char * p_chr , const String & p_str ) {
return p_str = = p_chr ;
}
String operator + ( const char * p_chr , const String & p_str ) {
String tmp = p_chr ;
tmp + = p_str ;
return tmp ;
}
String operator + ( CharType p_chr , const String & p_str ) {
return ( String : : chr ( p_chr ) + p_str ) ;
}
uint32_t String : : hash ( const char * p_cstr ) {
uint32_t hashv = 5381 ;
uint32_t c ;
while ( ( c = * p_cstr + + ) ) {
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
}
return hashv ;
}
uint32_t String : : hash ( const char * p_cstr , int p_len ) {
uint32_t hashv = 5381 ;
for ( int i = 0 ; i < p_len ; i + + ) {
hashv = ( ( hashv < < 5 ) + hashv ) + p_cstr [ i ] ; /* hash * 33 + c */
}
return hashv ;
}
uint32_t String : : hash ( const CharType * p_cstr , int p_len ) {
uint32_t hashv = 5381 ;
for ( int i = 0 ; i < p_len ; i + + ) {
hashv = ( ( hashv < < 5 ) + hashv ) + p_cstr [ i ] ; /* hash * 33 + c */
}
return hashv ;
}
uint32_t String : : hash ( const CharType * p_cstr ) {
uint32_t hashv = 5381 ;
uint32_t c ;
while ( ( c = * p_cstr + + ) ) {
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
}
return hashv ;
}
uint32_t String : : hash ( ) const {
/* simple djb2 hashing */
const CharType * chr = c_str ( ) ;
uint32_t hashv = 5381 ;
uint32_t c ;
while ( ( c = * chr + + ) ) {
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
}
return hashv ;
}
uint64_t String : : hash64 ( ) const {
/* simple djb2 hashing */
const CharType * chr = c_str ( ) ;
uint64_t hashv = 5381 ;
uint64_t c ;
while ( ( c = * chr + + ) ) {
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
}
return hashv ;
}
String String : : md5_text ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 16 ] ;
CryptoCore : : md5 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
return String : : hex_encode_buffer ( hash , 16 ) ;
}
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 ) ;
}
String String : : sha256_text ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 32 ] ;
CryptoCore : : sha256 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
return String : : hex_encode_buffer ( hash , 32 ) ;
}
Vector < uint8_t > String : : md5_buffer ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 16 ] ;
CryptoCore : : md5 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
Vector < uint8_t > ret ;
ret . resize ( 16 ) ;
for ( int i = 0 ; i < 16 ; i + + ) {
ret . write [ i ] = hash [ i ] ;
}
return ret ;
} ;
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 ;
}
Vector < uint8_t > String : : sha256_buffer ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 32 ] ;
CryptoCore : : sha256 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
Vector < uint8_t > ret ;
ret . resize ( 32 ) ;
for ( int i = 0 ; i < 32 ; i + + ) {
ret . write [ i ] = hash [ i ] ;
}
return ret ;
}
String String : : insert ( int p_at_pos , const String & p_string ) const {
if ( p_at_pos < 0 ) {
return * this ;
}
if ( p_at_pos > length ( ) ) {
p_at_pos = length ( ) ;
}
String pre ;
if ( p_at_pos > 0 ) {
pre = substr ( 0 , p_at_pos ) ;
}
String post ;
if ( p_at_pos < length ( ) ) {
post = substr ( p_at_pos , length ( ) - p_at_pos ) ;
}
return pre + p_string + post ;
}
String String : : substr ( int p_from , int p_chars ) const {
if ( p_chars = = - 1 ) {
p_chars = length ( ) - p_from ;
}
if ( empty ( ) | | p_from < 0 | | p_from > = length ( ) | | p_chars < = 0 ) {
return " " ;
}
if ( ( p_from + p_chars ) > length ( ) ) {
p_chars = length ( ) - p_from ;
}
if ( p_from = = 0 & & p_chars > = length ( ) ) {
return String ( * this ) ;
}
String s = String ( ) ;
s . copy_from_unchecked ( & c_str ( ) [ p_from ] , p_chars ) ;
return s ;
}
2022-06-26 03:38:28 +02:00
String String : : substr_index ( const int start_index , const int end_index ) const {
int s = length ( ) ;
if ( start_index < 0 | | start_index > = s | | end_index < 0 | | start_index > = s ) {
return " " ;
}
if ( start_index > end_index ) {
return " " ;
}
return substr ( start_index , end_index - start_index ) ;
}
2022-03-15 13:29:32 +01:00
int String : : find_last ( const String & p_str ) const {
return rfind ( p_str ) ;
}
int String : : find ( const String & p_str , int p_from ) const {
if ( p_from < 0 ) {
return - 1 ;
}
const int src_len = p_str . length ( ) ;
const int len = length ( ) ;
if ( src_len = = 0 | | len = = 0 ) {
return - 1 ; // won't find anything!
}
const CharType * src = c_str ( ) ;
const CharType * str = p_str . c_str ( ) ;
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 ;
} ;
if ( src [ read_pos ] ! = str [ j ] ) {
found = false ;
break ;
}
}
if ( found ) {
return i ;
}
}
return - 1 ;
}
int String : : find ( const char * p_str , int p_from ) const {
if ( p_from < 0 ) {
return - 1 ;
}
const int len = length ( ) ;
if ( len = = 0 ) {
return - 1 ; // won't find anything!
}
const CharType * src = c_str ( ) ;
int src_len = 0 ;
while ( p_str [ src_len ] ! = ' \0 ' ) {
src_len + + ;
}
if ( src_len = = 1 ) {
const char needle = p_str [ 0 ] ;
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 ;
} ;
if ( src [ read_pos ] ! = p_str [ j ] ) {
found = false ;
break ;
}
}
if ( found ) {
return i ;
}
}
}
return - 1 ;
}
int String : : find_char ( const CharType & p_char , int p_from ) const {
return _cowdata . find ( p_char , p_from ) ;
}
int String : : findmk ( const Vector < String > & p_keys , int p_from , int * r_key ) const {
if ( p_from < 0 ) {
return - 1 ;
}
if ( p_keys . size ( ) = = 0 ) {
return - 1 ;
}
//int src_len=p_str.length();
const String * keys = & p_keys [ 0 ] ;
int key_count = p_keys . size ( ) ;
int len = length ( ) ;
if ( len = = 0 ) {
return - 1 ; // won't find anything!
}
const CharType * src = c_str ( ) ;
for ( int i = p_from ; i < len ; i + + ) {
bool found = true ;
for ( int k = 0 ; k < key_count ; k + + ) {
found = true ;
if ( r_key ) {
* r_key = k ;
}
const CharType * cmp = keys [ k ] . c_str ( ) ;
int l = keys [ k ] . length ( ) ;
for ( int j = 0 ; j < l ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
found = false ;
break ;
} ;
if ( src [ read_pos ] ! = cmp [ j ] ) {
found = false ;
break ;
}
}
if ( found ) {
break ;
}
}
if ( found ) {
return i ;
}
}
return - 1 ;
}
int String : : findn ( const String & p_str , int p_from ) const {
if ( p_from < 0 ) {
return - 1 ;
}
int src_len = p_str . length ( ) ;
if ( src_len = = 0 | | length ( ) = = 0 ) {
return - 1 ; // won't find anything!
}
const CharType * srcd = c_str ( ) ;
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 ;
} ;
CharType src = _find_lower ( srcd [ read_pos ] ) ;
CharType dst = _find_lower ( p_str [ j ] ) ;
if ( src ! = dst ) {
found = false ;
break ;
}
}
if ( found ) {
return i ;
}
}
return - 1 ;
}
int String : : rfind ( const String & p_str , int p_from ) const {
// establish a limit
int limit = length ( ) - p_str . length ( ) ;
if ( limit < 0 ) {
return - 1 ;
}
// establish a starting point
if ( p_from < 0 ) {
p_from = limit ;
} else if ( p_from > limit ) {
p_from = limit ;
}
int src_len = p_str . length ( ) ;
int len = length ( ) ;
if ( src_len = = 0 | | len = = 0 ) {
return - 1 ; // won't find anything!
}
const CharType * src = c_str ( ) ;
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 ;
} ;
if ( src [ read_pos ] ! = p_str [ j ] ) {
found = false ;
break ;
}
}
if ( found ) {
return i ;
}
}
return - 1 ;
}
int String : : rfindn ( const String & p_str , int p_from ) const {
// establish a limit
int limit = length ( ) - p_str . length ( ) ;
if ( limit < 0 ) {
return - 1 ;
}
// establish a starting point
if ( p_from < 0 ) {
p_from = limit ;
} else if ( p_from > limit ) {
p_from = limit ;
}
int src_len = p_str . length ( ) ;
int len = length ( ) ;
if ( src_len = = 0 | | len = = 0 ) {
return - 1 ; // won't find anything!
}
const CharType * src = c_str ( ) ;
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 ;
} ;
CharType srcc = _find_lower ( src [ read_pos ] ) ;
CharType dstc = _find_lower ( p_str [ j ] ) ;
if ( srcc ! = dstc ) {
found = false ;
break ;
}
}
if ( found ) {
return i ;
}
}
return - 1 ;
}
2022-06-26 03:38:28 +02:00
int String : : find_first_difference_index ( const String & p_str ) const {
const int olen = p_str . length ( ) ;
const int len = length ( ) ;
const int c = len < olen ? len : olen ;
const CharType * p = c_str ( ) ;
const CharType * op = p_str . c_str ( ) ;
for ( int i = 0 ; i < c ; + + i ) {
if ( p [ i ] ! = op [ i ] ) {
return i ;
}
}
return c ;
}
bool String : : is_word_at ( const int index , const char * p_str ) const {
int size = length ( ) ;
ERR_FAIL_INDEX_V ( index , size , false ) ;
int i = 0 ;
while ( p_str [ i ] ! = ' \0 ' ) {
int iind = index + i ;
if ( iind > = size ) {
return false ;
}
if ( operator [ ] ( iind ) ! = p_str [ i ] ) {
return false ;
}
+ + i ;
}
return true ;
}
bool String : : is_word_at ( const int index , const String & p_str ) const {
int size = length ( ) ;
ERR_FAIL_INDEX_V ( index , size , false ) ;
if ( index + p_str . length ( ) > = size ) {
return false ;
}
for ( int i = 0 ; i < p_str . length ( ) ; + + i ) {
int iind = index + i ;
if ( operator [ ] ( iind ) ! = p_str [ i ] ) {
return false ;
}
}
return true ;
}
2022-03-15 13:29:32 +01:00
bool String : : ends_with ( const String & p_string ) const {
int l = p_string . length ( ) ;
if ( l > length ( ) ) {
return false ;
}
if ( l = = 0 ) {
return true ;
}
const CharType * p = & p_string [ 0 ] ;
const CharType * s = & operator [ ] ( length ( ) - l ) ;
for ( int i = 0 ; i < l ; i + + ) {
if ( p [ i ] ! = s [ i ] ) {
return false ;
}
}
return true ;
}
bool String : : begins_with ( const String & p_string ) const {
int l = p_string . length ( ) ;
if ( l > length ( ) ) {
return false ;
}
if ( l = = 0 ) {
return true ;
}
const CharType * p = & p_string [ 0 ] ;
const CharType * s = & operator [ ] ( 0 ) ;
for ( int i = 0 ; i < l ; i + + ) {
if ( p [ i ] ! = s [ i ] ) {
return false ;
}
}
return true ;
}
bool String : : begins_with ( const char * p_string ) const {
int l = length ( ) ;
if ( l = = 0 | | ! p_string ) {
return false ;
}
const CharType * str = & operator [ ] ( 0 ) ;
int i = 0 ;
while ( * p_string & & i < l ) {
if ( * p_string ! = str [ i ] ) {
return false ;
}
i + + ;
p_string + + ;
}
return * p_string = = 0 ;
}
bool String : : is_enclosed_in ( const String & p_string ) const {
return begins_with ( p_string ) & & ends_with ( p_string ) ;
}
bool String : : is_subsequence_of ( const String & p_string ) const {
return _base_is_subsequence_of ( p_string , false ) ;
}
bool String : : is_subsequence_ofi ( const String & p_string ) const {
return _base_is_subsequence_of ( p_string , true ) ;
}
bool String : : is_quoted ( ) const {
return is_enclosed_in ( " \" " ) | | is_enclosed_in ( " ' " ) ;
}
int String : : _count ( const String & p_string , int p_from , int p_to , bool p_case_insensitive ) const {
if ( p_string . empty ( ) ) {
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 ( ) ;
str . copy_from_unchecked ( & c_str ( ) [ 0 ] , len ) ;
} 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 ) ;
}
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 ;
}
const CharType * src = & operator [ ] ( 0 ) ;
const CharType * tgt = & p_string [ 0 ] ;
for ( ; * src & & * tgt ; tgt + + ) {
bool match = false ;
if ( case_insensitive ) {
CharType srcc = _find_lower ( * src ) ;
CharType tgtc = _find_lower ( * tgt ) ;
match = srcc = = tgtc ;
} else {
match = * src = = * tgt ;
}
if ( match ) {
src + + ;
if ( ! * src ) {
return true ;
}
}
}
return false ;
}
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 + + ) {
b . write [ i ] = substr ( i , 2 ) ;
}
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 ( ) ;
float sum = src_size + tgt_size ;
float inter = 0 ;
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 ;
}
static bool _wildcard_match ( const CharType * p_pattern , const CharType * p_string , bool p_case_sensitive ) {
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 :
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 ) ;
}
}
bool String : : match ( const String & p_wildcard ) const {
if ( ! p_wildcard . length ( ) | | ! length ( ) ) {
return false ;
}
return _wildcard_match ( p_wildcard . c_str ( ) , c_str ( ) , true ) ;
}
bool String : : matchn ( const String & p_wildcard ) const {
if ( ! p_wildcard . length ( ) | | ! length ( ) ) {
return false ;
}
return _wildcard_match ( p_wildcard . c_str ( ) , c_str ( ) , false ) ;
}
String String : : format ( const Variant & values , String placeholder ) const {
String new_string = String ( this - > ptr ( ) ) ;
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 ) ;
2022-03-23 20:46:05 +01:00
if ( values_arr [ i ] . get_type ( ) = = Variant : : ARRAY ) { //Array in Array structure [["name","RobotGuy"],[0,"pandemonium"],["strength",9000.91]]
2022-03-15 13:29:32 +01:00
Array value_arr = values_arr [ i ] ;
if ( value_arr . size ( ) = = 2 ) {
Variant v_key = value_arr [ 0 ] ;
String key = v_key ;
Variant v_val = value_arr [ 1 ] ;
String val = v_val ;
new_string = new_string . replace ( placeholder . replace ( " _ " , key ) , val ) ;
} 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 ] ;
String val = v_val ;
if ( placeholder . find ( " _ " ) > - 1 ) {
new_string = new_string . replace ( placeholder . replace ( " _ " , i_as_str ) , val ) ;
} else {
new_string = new_string . replace_first ( placeholder , val ) ;
}
}
}
} else if ( values . get_type ( ) = = Variant : : DICTIONARY ) {
Dictionary d = values ;
List < Variant > keys ;
d . get_key_list ( & keys ) ;
for ( List < Variant > : : Element * E = keys . front ( ) ; E ; E = E - > next ( ) ) {
String key = E - > get ( ) ;
String val = d [ E - > get ( ) ] ;
new_string = new_string . replace ( placeholder . replace ( " _ " , key ) , val ) ;
}
} else {
ERR_PRINT ( String ( " Invalid type: use Array or Dictionary. " ) . ascii ( ) . get_data ( ) ) ;
}
return new_string ;
}
String String : : replace ( const String & p_key , const String & 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 ;
search_from = result + p_key . length ( ) ;
}
if ( search_from = = 0 ) {
return * this ;
}
new_string + = substr ( search_from , length ( ) - search_from ) ;
return new_string ;
}
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 ;
while ( p_key [ k ] ! = ' \0 ' ) {
k + + ;
}
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 {
int pos = find ( p_key ) ;
if ( pos > = 0 ) {
return substr ( 0 , pos ) + p_with + substr ( pos + p_key . length ( ) , length ( ) ) ;
}
return * this ;
}
String String : : replacen ( const String & p_key , const String & p_with ) const {
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 ( ) ;
}
if ( search_from = = 0 ) {
return * this ;
}
new_string + = substr ( search_from , length ( ) - search_from ) ;
return new_string ;
}
2022-06-26 03:38:28 +02:00
String String : : newline_to_br ( ) const {
String r = replace ( " \r \n " , " <br> " ) ;
return r . replace ( " \n " , " <br> " ) ;
}
2022-03-15 13:29:32 +01:00
String String : : repeat ( int p_count ) const {
ERR_FAIL_COND_V_MSG ( p_count < 0 , " " , " Parameter count should be a positive number. " ) ;
String new_string ;
const CharType * src = this - > c_str ( ) ;
new_string . resize ( length ( ) * p_count + 1 ) ;
new_string [ length ( ) * p_count ] = 0 ;
for ( int i = 0 ; i < p_count ; i + + ) {
for ( int j = 0 ; j < length ( ) ; j + + ) {
new_string [ i * length ( ) + j ] = src [ j ] ;
}
}
return new_string ;
}
String String : : left ( int p_pos ) const {
if ( p_pos < = 0 ) {
return " " ;
}
if ( p_pos > = length ( ) ) {
return * this ;
}
return substr ( 0 , p_pos ) ;
}
String String : : right ( int p_pos ) const {
if ( p_pos > = length ( ) ) {
return " " ;
}
if ( p_pos < = 0 ) {
return * this ;
}
return substr ( p_pos , ( length ( ) - p_pos ) ) ;
}
CharType String : : ord_at ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , length ( ) , 0 ) ;
return operator [ ] ( p_idx ) ;
}
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 ;
}
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 + + ) {
CharType c = operator [ ] ( i ) ;
if ( c = = ' \n ' ) {
if ( has_text ) {
new_string + = substr ( indent_stop , i - indent_stop ) ;
}
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 ;
if ( j > = indent . length ( ) | | c ! = indent [ j ] ) {
indent_stop = i ;
}
}
}
}
if ( has_text ) {
new_string + = substr ( indent_stop , length ( ) - indent_stop ) ;
}
return new_string ;
}
String String : : strip_edges ( bool left , bool right ) const {
int len = length ( ) ;
int beg = 0 , end = len ;
if ( left ) {
for ( int i = 0 ; i < len ; i + + ) {
if ( operator [ ] ( i ) < = 32 ) {
beg + + ;
} else {
break ;
}
}
}
if ( right ) {
for ( int i = ( int ) ( len - 1 ) ; i > = 0 ; i - - ) {
if ( operator [ ] ( i ) < = 32 ) {
end - - ;
} else {
break ;
}
}
}
if ( beg = = 0 & & end = = len ) {
return * this ;
}
return substr ( beg , end - beg ) ;
}
String String : : strip_escapes ( ) const {
String new_string ;
for ( int i = 0 ; i < length ( ) ; i + + ) {
// Escape characters on first page of the ASCII table, before 32 (Space).
if ( operator [ ] ( i ) < 32 ) {
continue ;
}
new_string + = operator [ ] ( i ) ;
}
return new_string ;
}
String String : : lstrip ( const String & p_chars ) const {
int len = length ( ) ;
int beg ;
for ( beg = 0 ; beg < len ; beg + + ) {
if ( p_chars . find_char ( get ( beg ) ) = = - 1 ) {
break ;
}
}
if ( beg = = 0 ) {
return * this ;
}
return substr ( beg , len - beg ) ;
}
String String : : rstrip ( const String & p_chars ) const {
int len = length ( ) ;
int end ;
for ( end = len - 1 ; end > = 0 ; end - - ) {
if ( p_chars . find_char ( get ( end ) ) = = - 1 ) {
break ;
}
}
if ( end = = len - 1 ) {
return * this ;
}
return substr ( 0 , end + 1 ) ;
}
String String : : simplify_path ( ) const {
String s = * this ;
String drive ;
if ( s . begins_with ( " local:// " ) ) {
drive = " local:// " ;
s = s . substr ( 8 , s . length ( ) ) ;
} else if ( s . begins_with ( " res:// " ) ) {
drive = " res:// " ;
s = s . substr ( 6 , s . length ( ) ) ;
} else if ( s . begins_with ( " user:// " ) ) {
drive = " user:// " ;
s = s . substr ( 7 , s . length ( ) ) ;
} else if ( s . begins_with ( " / " ) | | s . begins_with ( " \\ " ) ) {
drive = s . substr ( 0 , 1 ) ;
s = s . substr ( 1 , s . length ( ) - 1 ) ;
} else {
int 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 , s . length ( ) ) ;
}
}
s = s . replace ( " \\ " , " / " ) ;
while ( true ) { // in case of using 2 or more slash
String compare = s . replace ( " // " , " / " ) ;
if ( s = = compare ) {
break ;
} else {
s = compare ;
}
}
Vector < String > dirs = s . split ( " / " , false ) ;
for ( int i = 0 ; i < dirs . size ( ) ; i + + ) {
String d = dirs [ i ] ;
if ( d = = " . " ) {
dirs . remove ( i ) ;
i - - ;
} else if ( d = = " .. " ) {
if ( i = = 0 ) {
dirs . remove ( i ) ;
i - - ;
} else {
dirs . remove ( i ) ;
dirs . remove ( i - 1 ) ;
i - = 2 ;
}
}
}
s = " " ;
for ( int i = 0 ; i < dirs . size ( ) ; i + + ) {
if ( i > 0 ) {
s + = " / " ;
}
s + = dirs [ i ] ;
}
return drive + s ;
}
2022-06-26 03:38:28 +02:00
String String : : append_path ( const char * path ) const {
if ( path [ 0 ] = = ' \0 ' ) {
return * this ;
}
String ret = * this ;
int size = length ( ) ;
if ( size = = 0 ) {
ret + = path ;
return ret ;
}
int sindex = 0 ;
char ch = path [ sindex ] ;
while ( ch = = ' / ' | | ch = = ' \\ ' ) {
if ( ch = = ' \0 ' ) {
return ret ;
}
ch = path [ + + sindex ] ;
}
// /////folder
// ^ (sindex)
if ( ret . ends_with ( " / " ) | | ret . ends_with ( " \\ " ) ) {
ret + = & path [ sindex ] ;
} else {
if ( sindex > 0 ) {
ret + = ' / ' ;
ret + = & path [ sindex - 1 ] ;
} else {
ret + = ' / ' ;
ret + = & path [ sindex ] ;
}
}
return ret ;
}
String String : : append_path ( const String & path ) const {
if ( path . length ( ) = = 0 ) {
return * this ;
}
int size = length ( ) ;
if ( size = = 0 ) {
return path ;
}
int sindex = 0 ;
int ts = path . size ( ) - 1 ;
char ch = path [ sindex ] ;
while ( ch = = ' / ' | | ch = = ' \\ ' ) {
if ( sindex = = ts ) {
return * this ;
}
ch = path [ + + sindex ] ;
}
String ret = * this ;
// /////folder
// ^ (sindex)
if ( ret . ends_with ( " / " ) | | ret . ends_with ( " \\ " ) ) {
ret + = & path [ sindex ] ;
} else {
if ( sindex > 0 ) {
ret + = ' / ' ;
ret + = & path [ sindex - 1 ] ;
} else {
ret + = ' / ' ;
ret + = & path [ sindex ] ;
}
}
return ret ;
}
String String : : path_clean_end_slash ( ) const {
// _size > 1, so if root is given ("/"), it will not be removed
String ret = * this ;
while ( ret . length ( ) > 1 & & ( ret . ends_with ( " / " ) | | ret . ends_with ( " \\ " ) ) ) {
2022-07-02 19:55:18 +02:00
ret . resize ( ret . length ( ) ) ;
2022-06-26 03:38:28 +02:00
}
return ret ;
}
String String : : path_ensure_end_slash ( ) const {
// Don't add if empty string, as it would make it root on linux, which can easily become a serious bug
String ret = * this ;
if ( ret . length ( ) = = 0 ) {
return ret ;
}
if ( ! ( ret . ends_with ( " / " ) | | ret . ends_with ( " \\ " ) ) ) {
ret + = " / " ;
}
return ret ;
}
String String : : path_get_prev_dir ( ) const {
int size = length ( ) ;
if ( size = = 0 ) {
return " / " ;
}
int seind = size - 1 ;
while ( seind > 0 & & ( operator [ ] ( seind ) = = ' / ' | | operator [ ] ( seind ) = = ' \\ ' ) ) {
- - seind ;
}
if ( seind = = 0 ) {
// /////////
// or
// a///////
// no prev dir
return " / " ;
}
// fol/fol2/fol3//
// ^ (seind)
while ( seind > 0 & & ( operator [ ] ( seind ) ! = ' / ' & & operator [ ] ( seind ) ! = ' \\ ' ) ) {
- - seind ;
}
// fol/fol2/fol3//
// ^ (seind)
//--seind;
if ( seind < = 0 ) {
return " / " ;
}
return substr_index ( 0 , seind ) ;
}
2022-03-15 13:29:32 +01:00
static int _humanize_digits ( int p_num ) {
if ( p_num < 100 ) {
return 2 ;
} else if ( p_num < 1024 ) {
return 1 ;
} else {
return 0 ;
}
}
String String : : humanize_size ( uint64_t p_size ) {
uint64_t _div = 1 ;
Vector < String > prefixes ;
prefixes . push_back ( RTR ( " B " ) ) ;
prefixes . push_back ( RTR ( " KiB " ) ) ;
prefixes . push_back ( RTR ( " MiB " ) ) ;
prefixes . push_back ( RTR ( " GiB " ) ) ;
prefixes . push_back ( RTR ( " TiB " ) ) ;
prefixes . push_back ( RTR ( " PiB " ) ) ;
prefixes . push_back ( RTR ( " EiB " ) ) ;
int prefix_idx = 0 ;
while ( prefix_idx < prefixes . size ( ) - 1 & & p_size > ( _div * 1024 ) ) {
_div * = 1024 ;
prefix_idx + + ;
}
const int digits = prefix_idx > 0 ? _humanize_digits ( p_size / _div ) : 0 ;
const double divisor = prefix_idx > 0 ? _div : 1 ;
return String : : num ( p_size / divisor ) . pad_decimals ( digits ) + " " + prefixes [ prefix_idx ] ;
}
bool String : : is_abs_path ( ) const {
if ( length ( ) > 1 ) {
return ( operator [ ] ( 0 ) = = ' / ' | | operator [ ] ( 0 ) = = ' \\ ' | | find ( " :/ " ) ! = - 1 | | find ( " : \\ " ) ! = - 1 ) ;
} else if ( ( length ( ) ) = = 1 ) {
return ( operator [ ] ( 0 ) = = ' / ' | | operator [ ] ( 0 ) = = ' \\ ' ) ;
} else {
return false ;
}
}
bool String : : is_valid_identifier ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
return false ;
}
const wchar_t * str = & operator [ ] ( 0 ) ;
for ( int i = 0 ; i < len ; i + + ) {
if ( i = = 0 ) {
if ( str [ 0 ] > = ' 0 ' & & str [ 0 ] < = ' 9 ' ) {
return false ; // no start with number plz
}
}
bool valid_char = ( str [ i ] > = ' 0 ' & & str [ i ] < = ' 9 ' ) | | ( str [ i ] > = ' a ' & & str [ i ] < = ' z ' ) | | ( str [ i ] > = ' A ' & & str [ i ] < = ' Z ' ) | | str [ i ] = = ' _ ' ;
if ( ! valid_char ) {
return false ;
}
}
return true ;
}
//kind of poor should be rewritten properly
String String : : word_wrap ( int p_chars_per_line ) const {
int from = 0 ;
int last_space = 0 ;
String ret ;
for ( int i = 0 ; i < length ( ) ; i + + ) {
if ( i - from > = p_chars_per_line ) {
if ( last_space = = - 1 ) {
ret + = substr ( from , i - from + 1 ) + " \n " ;
} else {
ret + = substr ( from , last_space - from ) + " \n " ;
i = last_space ; //rewind
}
from = i + 1 ;
last_space = - 1 ;
} else if ( operator [ ] ( i ) = = ' ' | | operator [ ] ( i ) = = ' \t ' ) {
last_space = i ;
} else if ( operator [ ] ( i ) = = ' \n ' ) {
ret + = substr ( from , i - from ) + " \n " ;
from = i + 1 ;
last_space = - 1 ;
}
}
if ( from < length ( ) ) {
ret + = substr ( from , length ( ) ) ;
}
return ret ;
}
String String : : http_escape ( ) const {
const CharString temp = utf8 ( ) ;
String res ;
for ( int i = 0 ; i < temp . length ( ) ; + + i ) {
char ord = temp [ i ] ;
if ( ord = = ' . ' | | ord = = ' - ' | | ord = = ' _ ' | | ord = = ' ~ ' | |
( ord > = ' a ' & & ord < = ' z ' ) | |
( ord > = ' A ' & & ord < = ' Z ' ) | |
( ord > = ' 0 ' & & ord < = ' 9 ' ) ) {
res + = ord ;
} else {
char h_Val [ 3 ] ;
# if defined(__GNUC__) || defined(_MSC_VER)
snprintf ( h_Val , 3 , " %02hhX " , ord ) ;
# else
sprintf ( h_Val , " %02hhX " , ord ) ;
# endif
res + = " % " ;
res + = h_Val ;
}
}
return res ;
}
String String : : http_unescape ( ) const {
String res ;
for ( int i = 0 ; i < length ( ) ; + + i ) {
if ( ord_at ( i ) = = ' % ' & & i + 2 < length ( ) ) {
CharType ord1 = ord_at ( i + 1 ) ;
if ( ( ord1 > = ' 0 ' & & ord1 < = ' 9 ' ) | | ( ord1 > = ' A ' & & ord1 < = ' Z ' ) ) {
CharType ord2 = ord_at ( i + 2 ) ;
if ( ( ord2 > = ' 0 ' & & ord2 < = ' 9 ' ) | | ( ord2 > = ' A ' & & ord2 < = ' Z ' ) ) {
char bytes [ 3 ] = { ( char ) ord1 , ( char ) ord2 , 0 } ;
res + = ( char ) strtol ( bytes , nullptr , 16 ) ;
i + = 2 ;
}
} else {
res + = ord_at ( i ) ;
}
} else {
res + = ord_at ( i ) ;
}
}
return String : : utf8 ( res . ascii ( ) ) ;
}
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 ( " \\ ? " , " \ ? " ) ;
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 ( " \' " , " \\ ' " ) ;
escaped = escaped . replace ( " \ ? " , " \\ ? " ) ;
escaped = escaped . replace ( " \" " , " \\ \" " ) ;
return escaped ;
}
String String : : c_escape_multiline ( ) const {
String escaped = * this ;
escaped = escaped . replace ( " \\ " , " \\ \\ " ) ;
escaped = escaped . replace ( " \" " , " \\ \" " ) ;
return escaped ;
}
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 ;
}
String String : : xml_escape ( bool p_escape_quotes ) const {
String str = * this ;
str = str . replace ( " & " , " & " ) ;
str = str . replace ( " < " , " < " ) ;
str = str . replace ( " > " , " > " ) ;
if ( p_escape_quotes ) {
str = str . replace ( " ' " , " ' " ) ;
str = str . replace ( " \" " , " " " ) ;
}
/*
for ( int i = 1 ; i < 32 ; i + + ) {
char chr [ 2 ] = { i , 0 } ;
str = str . replace ( chr , " &# " + String : : num ( i ) + " ; " ) ;
} */
return str ;
}
static _FORCE_INLINE_ int _xml_unescape ( const CharType * p_src , int p_src_len , CharType * p_dst ) {
int len = 0 ;
while ( p_src_len ) {
if ( * p_src = = ' & ' ) {
int eat = 0 ;
if ( p_src_len > = 4 & & p_src [ 1 ] = = ' # ' ) {
CharType c = 0 ;
bool overflow = false ;
if ( p_src [ 2 ] = = ' x ' ) {
// Hex entity &#x<num>;
for ( int i = 3 ; i < p_src_len ; i + + ) {
eat = i + 1 ;
CharType ct = p_src [ i ] ;
if ( ct = = ' ; ' ) {
break ;
} else if ( ct > = ' 0 ' & & ct < = ' 9 ' ) {
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 > ( WCHAR_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 ;
CharType ct = p_src [ i ] ;
if ( ct = = ' ; ' | | ct < ' 0 ' | | ct > ' 9 ' ) {
break ;
}
}
if ( p_src [ eat - 1 ] = = ' ; ' ) {
int64_t val = String : : to_int ( p_src + 2 , eat - 3 ) ;
if ( val > 0 & & val < = WCHAR_MAX ) {
c = ( CharType ) val ;
} else {
overflow = true ;
}
}
}
// 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 ;
}
if ( p_dst ) {
* p_dst = c ;
}
} else if ( p_src_len > = 4 & & p_src [ 1 ] = = ' g ' & & p_src [ 2 ] = = ' t ' & & p_src [ 3 ] = = ' ; ' ) {
if ( p_dst ) {
* p_dst = ' > ' ;
}
eat = 4 ;
} else if ( p_src_len > = 4 & & p_src [ 1 ] = = ' l ' & & p_src [ 2 ] = = ' t ' & & p_src [ 3 ] = = ' ; ' ) {
if ( p_dst ) {
* p_dst = ' < ' ;
}
eat = 4 ;
} else if ( p_src_len > = 5 & & p_src [ 1 ] = = ' a ' & & p_src [ 2 ] = = ' m ' & & p_src [ 3 ] = = ' p ' & & p_src [ 4 ] = = ' ; ' ) {
if ( p_dst ) {
* p_dst = ' & ' ;
}
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 ] = = ' ; ' ) {
if ( p_dst ) {
* p_dst = ' " ' ;
}
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 ] = = ' ; ' ) {
if ( p_dst ) {
* p_dst = ' \' ' ;
}
eat = 6 ;
} else {
if ( p_dst ) {
* p_dst = * p_src ;
}
eat = 1 ;
}
if ( p_dst ) {
p_dst + + ;
}
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 ;
}
String String : : xml_unescape ( ) const {
String str ;
int l = length ( ) ;
int len = _xml_unescape ( c_str ( ) , l , nullptr ) ;
if ( len = = 0 ) {
return String ( ) ;
}
str . resize ( len + 1 ) ;
_xml_unescape ( c_str ( ) , l , str . ptrw ( ) ) ;
str [ len ] = 0 ;
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 ) {
s = s . substr ( 0 , c + p_digits + 1 ) ;
} else {
while ( s . length ( ) - ( c + 1 ) < p_digits ) {
s + = " 0 " ;
}
}
return s ;
}
String String : : pad_zeros ( int p_digits ) const {
String s = * this ;
int end = s . find ( " . " ) ;
if ( end = = - 1 ) {
end = s . length ( ) ;
}
if ( end = = 0 ) {
return s ;
}
int begin = 0 ;
while ( begin < end & & ( s [ begin ] < ' 0 ' | | s [ begin ] > ' 9 ' ) ) {
begin + + ;
}
if ( begin > = end ) {
return s ;
}
while ( end - begin < p_digits ) {
s = s . insert ( begin , " 0 " ) ;
end + + ;
}
return s ;
}
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 ;
}
bool String : : is_valid_integer ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
return false ;
}
int from = 0 ;
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) ) {
from + + ;
}
for ( int i = from ; i < len ; i + + ) {
if ( operator [ ] ( i ) < ' 0 ' | | operator [ ] ( i ) > ' 9 ' ) {
return false ; // no start with number plz
}
}
return true ;
}
bool String : : is_valid_hex_number ( bool p_with_prefix ) const {
int len = length ( ) ;
if ( len = = 0 ) {
return false ;
}
int from = 0 ;
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) ) {
from + + ;
}
if ( p_with_prefix ) {
if ( len < 3 ) {
return false ;
}
if ( operator [ ] ( from ) ! = ' 0 ' | | operator [ ] ( from + 1 ) ! = ' x ' ) {
return false ;
}
from + = 2 ;
}
for ( int i = from ; i < len ; i + + ) {
CharType c = operator [ ] ( i ) ;
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | ( c > = ' a ' & & c < = ' f ' ) | | ( c > = ' A ' & & c < = ' F ' ) ) {
continue ;
}
return false ;
}
return true ;
} ;
bool String : : is_valid_float ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
return false ;
}
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 + + ) {
if ( operator [ ] ( i ) > = ' 0 ' & & operator [ ] ( i ) < = ' 9 ' ) {
if ( exponent_found ) {
exponent_values_found = true ;
} else {
numbers_found = true ;
}
} 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 ;
} else {
return false ; // no start with number plz
}
}
return numbers_found ;
}
2022-06-26 03:38:28 +02:00
bool String : : is_valid_bool ( ) const {
int size = length ( ) ;
if ( size = = 1 ) {
CharType c = ptr ( ) [ 0 ] ;
if ( c = = ' 0 ' ) {
return true ;
} else if ( c = = ' 1 ' ) {
return true ;
}
return false ;
} else if ( size = = 4 ) {
String l = to_lower ( ) ;
const CharType * p = l . ptr ( ) ;
if ( p [ 0 ] = = ' t ' & & p [ 1 ] = = ' r ' & & p [ 2 ] = = ' u ' & & p [ 3 ] = = ' e ' ) {
return true ;
} else {
return false ;
}
} else if ( size = = 5 ) {
String l = to_lower ( ) ;
const CharType * p = l . ptr ( ) ;
if ( p [ 0 ] = = ' f ' & & p [ 1 ] = = ' a ' & & p [ 2 ] = = ' l ' & & p [ 3 ] = = ' s ' & & p [ 3 ] = = ' e ' ) {
return true ;
} else {
return false ;
}
}
return false ;
}
bool String : : is_valid_unsigned_integer ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
return false ;
}
int from = 0 ;
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' ) ) {
from + + ;
}
for ( int i = from ; i < len ; i + + ) {
if ( operator [ ] ( i ) < ' 0 ' | | operator [ ] ( i ) > ' 9 ' ) {
return false ; // no start with number plz
}
}
return true ;
}
2022-03-15 13:29:32 +01:00
String String : : path_to_file ( const String & p_path ) const {
// Don't get base dir for src, this is expected to be a dir already.
String src = this - > replace ( " \\ " , " / " ) ;
String dst = p_path . replace ( " \\ " , " / " ) . get_base_dir ( ) ;
String rel = src . path_to ( dst ) ;
if ( rel = = dst ) { // failed
return p_path ;
} else {
return rel + p_path . get_file ( ) ;
}
}
String String : : path_to ( const String & p_path ) const {
String src = this - > replace ( " \\ " , " / " ) ;
String dst = p_path . replace ( " \\ " , " / " ) ;
if ( ! src . ends_with ( " / " ) ) {
src + = " / " ;
}
if ( ! dst . ends_with ( " / " ) ) {
dst + = " / " ;
}
String base ;
if ( src . begins_with ( " res:// " ) & & dst . begins_with ( " res:// " ) ) {
base = " res:/ " ;
src = src . replace ( " res:// " , " / " ) ;
dst = dst . replace ( " res:// " , " / " ) ;
} else if ( src . begins_with ( " user:// " ) & & dst . begins_with ( " user:// " ) ) {
base = " user:/ " ;
src = src . replace ( " user:// " , " / " ) ;
dst = dst . replace ( " user:// " , " / " ) ;
} else if ( src . begins_with ( " / " ) & & dst . begins_with ( " / " ) ) {
//nothing
} else {
//dos style
String src_begin = src . get_slicec ( ' / ' , 0 ) ;
String dst_begin = dst . get_slicec ( ' / ' , 0 ) ;
if ( src_begin ! = dst_begin ) {
return p_path ; //impossible to do this
}
base = src_begin ;
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 ) {
if ( src_dirs . size ( ) = = common_parent ) {
break ;
}
if ( dst_dirs . size ( ) = = common_parent ) {
break ;
}
if ( src_dirs [ common_parent ] ! = dst_dirs [ common_parent ] ) {
break ;
}
common_parent + + ;
}
common_parent - - ;
String dir ;
for ( int i = src_dirs . size ( ) - 1 ; i > common_parent ; i - - ) {
dir + = " ../ " ;
}
for ( int i = common_parent + 1 ; i < dst_dirs . size ( ) ; i + + ) {
dir + = dst_dirs [ i ] + " / " ;
}
if ( dir . length ( ) = = 0 ) {
dir = " ./ " ;
}
return dir ;
}
bool String : : is_valid_html_color ( ) const {
return Color : : html_is_valid ( * this ) ;
}
bool String : : is_valid_filename ( ) const {
String stripped = strip_edges ( ) ;
if ( * this ! = stripped ) {
return false ;
}
if ( stripped = = String ( ) ) {
return false ;
}
return ! ( find ( " : " ) ! = - 1 | | find ( " / " ) ! = - 1 | | find ( " \\ " ) ! = - 1 | | find ( " ? " ) ! = - 1 | | find ( " * " ) ! = - 1 | | find ( " \" " ) ! = - 1 | | find ( " | " ) ! = - 1 | | find ( " % " ) ! = - 1 | | find ( " < " ) ! = - 1 | | find ( " > " ) ! = - 1 ) ;
}
bool String : : is_valid_ip_address ( ) const {
if ( find ( " : " ) > = 0 ) {
Vector < String > ip = split ( " : " ) ;
for ( int i = 0 ; i < ip . size ( ) ; i + + ) {
String n = ip [ i ] ;
if ( n . empty ( ) ) {
continue ;
}
if ( n . is_valid_hex_number ( false ) ) {
int nint = n . hex_to_int ( false ) ;
if ( nint < 0 | | nint > 0xffff ) {
return false ;
}
continue ;
} ;
if ( ! n . is_valid_ip_address ( ) ) {
return false ;
}
} ;
} else {
Vector < String > ip = split ( " . " ) ;
if ( ip . size ( ) ! = 4 ) {
return false ;
}
for ( int i = 0 ; i < ip . size ( ) ; i + + ) {
String n = ip [ i ] ;
if ( ! n . is_valid_integer ( ) ) {
return false ;
}
int val = n . to_int ( ) ;
if ( val < 0 | | val > 255 ) {
return false ;
}
}
} ;
return true ;
}
bool String : : is_resource_file ( ) const {
return begins_with ( " res:// " ) & & find ( " :: " ) = = - 1 ;
}
bool String : : is_rel_path ( ) const {
return ! is_abs_path ( ) ;
}
String String : : get_base_dir ( ) const {
int end = 0 ;
// url scheme style base
int basepos = find ( " :// " ) ;
if ( basepos ! = - 1 ) {
end = basepos + 3 ;
}
// windows top level directory base
if ( end = = 0 ) {
basepos = find ( " :/ " ) ;
if ( basepos = = - 1 ) {
basepos = find ( " : \\ " ) ;
}
if ( basepos ! = - 1 ) {
end = basepos + 2 ;
}
}
// unix root directory base
if ( end = = 0 ) {
if ( begins_with ( " / " ) ) {
end = 1 ;
}
}
String rs ;
String base ;
if ( end ! = 0 ) {
rs = substr ( end , length ( ) ) ;
base = substr ( 0 , end ) ;
} else {
rs = * this ;
}
int sep = MAX ( rs . rfind ( " / " ) , rs . rfind ( " \\ " ) ) ;
if ( sep = = - 1 ) {
return base ;
}
return base + rs . substr ( 0 , sep ) ;
}
String String : : get_file ( ) const {
int sep = MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ;
if ( sep = = - 1 ) {
return * this ;
}
return substr ( sep + 1 , length ( ) ) ;
}
String String : : get_extension ( ) const {
int pos = rfind ( " . " ) ;
if ( pos < 0 | | pos < MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ) {
return " " ;
}
return substr ( pos + 1 , length ( ) ) ;
}
String String : : plus_file ( const String & p_file ) const {
if ( empty ( ) ) {
return p_file ;
}
if ( operator [ ] ( length ( ) - 1 ) = = ' / ' | | ( p_file . size ( ) > 0 & & p_file . operator [ ] ( 0 ) = = ' / ' ) ) {
return * this + p_file ;
}
return * this + " / " + p_file ;
}
String String : : percent_encode ( ) const {
CharString cs = utf8 ( ) ;
String encoded ;
for ( int i = 0 ; i < cs . length ( ) ; i + + ) {
uint8_t c = cs [ i ] ;
if ( ( c > = ' A ' & & c < = ' Z ' ) | | ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' - ' | | c = = ' _ ' | | c = = ' ~ ' | | c = = ' . ' ) {
char p [ 2 ] = { ( char ) c , 0 } ;
encoded + = p ;
} else {
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 [ c > > 4 ] ;
p [ 2 ] = hex [ c & 0xF ] ;
encoded + = p ;
}
}
return encoded ;
}
String String : : percent_decode ( ) const {
CharString pe ;
CharString cs = utf8 ( ) ;
for ( int i = 0 ; i < cs . length ( ) ; i + + ) {
uint8_t c = cs [ i ] ;
if ( c = = ' % ' & & i < length ( ) - 2 ) {
uint8_t a = LOWERCASE ( cs [ i + 1 ] ) ;
uint8_t b = LOWERCASE ( cs [ i + 2 ] ) ;
if ( a > = ' 0 ' & & a < = ' 9 ' ) {
c = ( a - ' 0 ' ) < < 4 ;
} else if ( a > = ' a ' & & a < = ' f ' ) {
c = ( a - ' a ' + 10 ) < < 4 ;
} else {
continue ;
}
uint8_t d = 0 ;
if ( b > = ' 0 ' & & b < = ' 9 ' ) {
d = ( b - ' 0 ' ) ;
} else if ( b > = ' a ' & & b < = ' f ' ) {
d = ( b - ' a ' + 10 ) ;
} else {
continue ;
}
c + = d ;
i + = 2 ;
}
pe + = c ;
}
return String : : utf8 ( pe . ptr ( ) ) ;
}
String String : : property_name_encode ( ) const {
// Escape and quote strings with extended ASCII or further Unicode characters
// as well as '"', '=' or ' ' (32)
const CharType * cstr = c_str ( ) ;
for ( int i = 0 ; cstr [ i ] ; i + + ) {
if ( cstr [ i ] = = ' = ' | | cstr [ i ] = = ' " ' | | cstr [ i ] = = ' ; ' | | cstr [ i ] = = ' [ ' | | cstr [ i ] = = ' ] ' | | cstr [ i ] < 33 | | cstr [ i ] > 126 ) {
return " \" " + c_escape_multiline ( ) + " \" " ;
}
}
// Keep as is
return * this ;
}
// Changes made to the set of invalid characters must also be reflected in the String documentation.
const String String : : invalid_node_name_characters = " . : @ / \" " ;
String String : : validate_node_name ( ) const {
Vector < String > chars = String : : invalid_node_name_characters . split ( " " ) ;
String name = this - > replace ( chars [ 0 ] , " " ) ;
for ( int i = 1 ; i < chars . size ( ) ; i + + ) {
name = name . replace ( chars [ i ] , " " ) ;
}
return name ;
}
String String : : get_basename ( ) const {
int pos = rfind ( " . " ) ;
if ( pos < 0 | | pos < MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ) {
return * this ;
}
return substr ( 0 , pos ) ;
}
String itos ( int64_t p_val ) {
return String : : num_int64 ( p_val ) ;
}
String uitos ( uint64_t p_val ) {
return String : : num_uint64 ( p_val ) ;
}
String rtos ( double p_val ) {
return String : : num ( p_val ) ;
}
String rtoss ( double p_val ) {
return String : : num_scientific ( p_val ) ;
}
// 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 ) {
for ( int i = 0 ; i < padding ; i + + ) {
s = s + character ;
}
}
return s ;
}
// 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 ) {
for ( int i = 0 ; i < padding ; i + + ) {
s = character + s ;
}
}
return s ;
}
// sprintf is implemented in GDScript via:
// "fish %s pie" % "frog"
// "fish %s %d pie" % ["frog", 12]
// 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 {
String formatted ;
CharType * self = ( CharType * ) c_str ( ) ;
bool in_format = false ;
int value_index = 0 ;
int min_chars = 0 ;
int min_decimals = 0 ;
bool in_decimals = false ;
bool pad_with_zeroes = false ;
bool left_justified = false ;
bool show_sign = false ;
* error = true ;
for ( ; * self ; self + + ) {
const CharType c = * self ;
if ( in_format ) { // We have % - lets see what else we get.
switch ( c ) {
case ' % ' : { // Replace %% with %
formatted + = chr ( c ) ;
in_format = false ;
break ;
}
case ' d ' : // Integer (signed)
case ' o ' : // Octal
case ' x ' : // Hexadecimal (lowercase)
case ' X ' : { // Hexadecimal (uppercase)
if ( value_index > = values . size ( ) ) {
return " not enough arguments for format string " ;
}
if ( ! values [ value_index ] . is_num ( ) ) {
return " a number is required " ;
}
int64_t value = values [ value_index ] ;
int base = 16 ;
bool capitalize = false ;
switch ( c ) {
case ' d ' :
base = 10 ;
break ;
case ' o ' :
base = 8 ;
break ;
case ' x ' :
break ;
case ' X ' :
base = 16 ;
capitalize = true ;
break ;
}
// Get basic number.
String str = String : : num_int64 ( ABS ( value ) , base , capitalize ) ;
int number_len = str . length ( ) ;
// Padding.
int pad_chars_count = ( value < 0 | | show_sign ) ? min_chars - 1 : min_chars ;
String pad_char = pad_with_zeroes ? String ( " 0 " ) : String ( " " ) ;
if ( left_justified ) {
str = str . rpad ( pad_chars_count , pad_char ) ;
} else {
str = str . lpad ( pad_chars_count , pad_char ) ;
}
// Sign.
if ( show_sign & & value > = 0 ) {
str = str . insert ( pad_with_zeroes ? 0 : str . length ( ) - number_len , " + " ) ;
} else if ( value < 0 ) {
str = str . insert ( pad_with_zeroes ? 0 : str . length ( ) - number_len , " - " ) ;
}
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
case ' f ' : { // Float
if ( value_index > = values . size ( ) ) {
return " not enough arguments for format string " ;
}
if ( ! values [ value_index ] . is_num ( ) ) {
return " a number is required " ;
}
double value = values [ value_index ] ;
bool is_negative = ( value < 0 ) ;
String str = String : : num ( ABS ( value ) , min_decimals ) ;
// Pad decimals out.
str = str . pad_decimals ( min_decimals ) ;
int initial_len = str . length ( ) ;
// Padding. Leave room for sign later if required.
int pad_chars_count = ( is_negative | | show_sign ) ? min_chars - 1 : min_chars ;
String pad_char = pad_with_zeroes ? String ( " 0 " ) : String ( " " ) ;
if ( left_justified ) {
if ( pad_with_zeroes ) {
return " left justification cannot be used with zeros as the padding " ;
} else {
str = str . rpad ( pad_chars_count , pad_char ) ;
}
} else {
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 {
str = str . insert ( pad_with_zeroes ? 0 : str . length ( ) - initial_len , sign_char ) ;
}
}
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
case ' s ' : { // String
if ( value_index > = values . size ( ) ) {
return " not enough arguments for format string " ;
}
String str = values [ value_index ] ;
// Padding.
if ( left_justified ) {
str = str . rpad ( min_chars ) ;
} else {
str = str . lpad ( min_chars ) ;
}
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
case ' c ' : {
if ( value_index > = values . size ( ) ) {
return " not enough arguments for format string " ;
}
// Convert to character.
String str ;
if ( values [ value_index ] . is_num ( ) ) {
int value = values [ value_index ] ;
if ( value < 0 ) {
return " unsigned byte integer is lower than maximum " ;
} else if ( value > 255 ) {
return " unsigned byte integer is greater than maximum " ;
}
str = chr ( values [ value_index ] ) ;
} else if ( values [ value_index ] . get_type ( ) = = Variant : : STRING ) {
str = values [ value_index ] ;
if ( str . length ( ) ! = 1 ) {
return " %c requires number or single-character string " ;
}
} else {
return " %c requires number or single-character string " ;
}
// Padding.
if ( left_justified ) {
str = str . rpad ( min_chars ) ;
} else {
str = str . lpad ( min_chars ) ;
}
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
case ' - ' : { // Left justify
left_justified = true ;
break ;
}
case ' + ' : { // Show + if positive.
show_sign = true ;
break ;
}
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 ) {
min_decimals * = 10 ;
min_decimals + = n ;
} else {
if ( c = = ' 0 ' & & min_chars = = 0 ) {
pad_with_zeroes = true ;
} else {
min_chars * = 10 ;
min_chars + = n ;
}
}
break ;
}
case ' . ' : { // Float separator.
if ( in_decimals ) {
return " too many decimal points in format " ;
}
in_decimals = true ;
min_decimals = 0 ; // We want to add the value manually.
break ;
}
case ' * ' : { // Dynamic width, based on value.
if ( value_index > = values . size ( ) ) {
return " not enough arguments for format string " ;
}
if ( ! values [ value_index ] . is_num ( ) ) {
return " * wants number " ;
}
int size = values [ value_index ] ;
if ( in_decimals ) {
min_decimals = size ;
} else {
min_chars = size ;
}
+ + value_index ;
break ;
}
default : {
return " unsupported format character " ;
}
}
} else { // Not in format string.
switch ( c ) {
case ' % ' :
in_format = true ;
// Back to defaults:
min_chars = 0 ;
min_decimals = 6 ;
pad_with_zeroes = false ;
left_justified = false ;
show_sign = false ;
in_decimals = false ;
break ;
default :
formatted + = chr ( c ) ;
}
}
}
if ( in_format ) {
return " incomplete format " ;
}
if ( value_index ! = values . size ( ) ) {
return " not all arguments converted during string formatting " ;
}
* error = false ;
return formatted ;
}
String String : : quote ( String quotechar ) const {
return quotechar + * this + quotechar ;
}
String String : : unquote ( ) const {
if ( ! is_quoted ( ) ) {
return * this ;
}
return substr ( 1 , length ( ) - 2 ) ;
}
# ifdef TOOLS_ENABLED
String TTR ( const String & p_text ) {
if ( TranslationServer : : get_singleton ( ) ) {
return TranslationServer : : get_singleton ( ) - > tool_translate ( p_text ) ;
}
return p_text ;
}
/* DTR is used for the documentation, 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 .
*/
String DTR ( const String & p_text ) {
// Comes straight from the XML, so remove indentation and any trailing whitespace.
const String text = p_text . dedent ( ) . strip_edges ( ) ;
if ( TranslationServer : : get_singleton ( ) ) {
return String ( TranslationServer : : get_singleton ( ) - > doc_translate ( text ) ) . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
}
return text . replace ( " $DOCS_URL " , VERSION_DOCS_URL ) ;
}
# endif
String RTR ( const String & p_text ) {
if ( TranslationServer : : get_singleton ( ) ) {
String rtr = TranslationServer : : get_singleton ( ) - > tool_translate ( p_text ) ;
if ( rtr = = String ( ) | | rtr = = p_text ) {
return TranslationServer : : get_singleton ( ) - > translate ( p_text ) ;
} else {
return rtr ;
}
}
return p_text ;
}