2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* ustring.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "ustring.h"
2017-01-16 08:04:19 +01:00
2019-07-11 15:21:47 +02:00
# include "core/crypto/crypto_core.h"
2020-11-07 23:33:38 +01:00
# include "core/math/color.h"
2018-09-11 18:13:45 +02:00
# include "core/math/math_funcs.h"
# include "core/os/memory.h"
2020-11-07 23:33:38 +01:00
# include "core/string/print_string.h"
# include "core/string/translation.h"
# include "core/string/ucaps.h"
# include "core/variant/variant.h"
2014-02-10 02:10:30 +01:00
2019-08-22 17:22:35 +02:00
# include <cstdint>
2014-02-10 02:10:30 +01:00
# ifndef NO_USE_STDLIB
# include <stdio.h>
2017-03-05 16:44:50 +01:00
# include <stdlib.h>
2014-02-10 02:10:30 +01:00
# endif
2015-11-19 22:09:57 +01:00
2020-05-12 17:01:17 +02:00
# ifdef _MSC_VER
# define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
# endif
2015-11-19 22:09:57 +01:00
# if defined(MINGW_ENABLED) || defined(_MSC_VER)
2018-10-04 15:38:52 +02:00
# define snprintf _snprintf_s
2015-11-19 22:09:57 +01:00
# endif
2017-01-16 08:04:19 +01:00
# define MAX_DIGITS 6
2017-03-05 16:44:50 +01:00
# 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))
2017-05-11 21:07:59 +02:00
# 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'))
2017-02-21 04:05:15 +01:00
2019-01-04 17:01:54 +01:00
const char CharString : : _null = 0 ;
2020-07-27 12:43:20 +02:00
const char16_t Char16String : : _null = 0 ;
const char32_t String : : _null = 0 ;
2019-01-04 17:01:54 +01:00
2020-07-27 12:43:20 +02:00
bool is_symbol ( char32_t c ) {
2017-12-23 09:59:54 +01:00
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 + + ;
}
2020-05-14 16:41:43 +02:00
if ( end < s . length ( ) ) {
2017-12-23 09:59:54 +01:00
end + = 1 ;
2020-05-14 16:41:43 +02:00
}
2017-12-23 09:59:54 +01:00
r_beg = beg ;
r_end = end ;
return true ;
} else {
return false ;
}
}
2020-07-27 12:43:20 +02:00
/*************************************************************************/
/* Char16String */
/*************************************************************************/
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
bool Char16String : : operator < ( const Char16String & p_right ) const {
2017-03-05 16:44:50 +01:00
if ( length ( ) = = 0 ) {
return p_right . length ( ) ! = 0 ;
2017-02-21 04:05:15 +01:00
}
2017-12-16 15:31:30 +01:00
return is_str_less ( get_data ( ) , p_right . get_data ( ) ) ;
2017-02-21 04:05:15 +01:00
}
2020-07-27 12:43:20 +02:00
Char16String & Char16String : : operator + = ( char16_t p_char ) {
2018-07-25 03:11:03 +02:00
resize ( size ( ) ? size ( ) + 1 : 2 ) ;
set ( length ( ) , 0 ) ;
set ( length ( ) - 1 , p_char ) ;
return * this ;
}
2020-07-27 12:43:20 +02:00
Char16String & Char16String : : operator = ( const char16_t * p_cstr ) {
copy_from ( p_cstr ) ;
return * this ;
}
const char16_t * Char16String : : get_data ( ) const {
2020-05-14 16:41:43 +02:00
if ( size ( ) ) {
2014-02-10 02:10:30 +01:00
return & operator [ ] ( 0 ) ;
2020-05-14 16:41:43 +02:00
} else {
2020-07-27 12:43:20 +02:00
return u " " ;
}
}
void Char16String : : copy_from ( const char16_t * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
const char16_t * s = p_cstr ;
for ( ; * s ; s + + ) {
}
size_t len = s - p_cstr ;
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
Error err = resize ( + + len ) ; // include terminating null char
ERR_FAIL_COND_MSG ( err ! = OK , " Failed to copy char16_t string. " ) ;
memcpy ( ptrw ( ) , p_cstr , len * sizeof ( char16_t ) ) ;
}
/*************************************************************************/
/* CharString */
/*************************************************************************/
bool CharString : : operator < ( const CharString & p_right ) const {
if ( length ( ) = = 0 ) {
return p_right . length ( ) ! = 0 ;
}
return is_str_less ( get_data ( ) , p_right . get_data ( ) ) ;
}
CharString & CharString : : operator + = ( char p_char ) {
resize ( size ( ) ? size ( ) + 1 : 2 ) ;
set ( length ( ) , 0 ) ;
set ( length ( ) - 1 , p_char ) ;
return * this ;
2014-02-10 02:10:30 +01:00
}
2019-03-12 13:57:22 +01:00
CharString & CharString : : operator = ( const char * p_cstr ) {
copy_from ( p_cstr ) ;
return * this ;
}
2020-07-27 12:43:20 +02:00
const char * CharString : : get_data ( ) const {
if ( size ( ) ) {
return & operator [ ] ( 0 ) ;
} else {
return " " ;
}
}
2019-03-12 13:57:22 +01:00
void CharString : : copy_from ( const char * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
size_t len = strlen ( p_cstr ) ;
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
2019-11-18 21:51:54 +01:00
Error err = resize ( + + len ) ; // include terminating null char
2019-03-12 13:57:22 +01:00
2019-11-18 21:51:54 +01:00
ERR_FAIL_COND_MSG ( err ! = OK , " Failed to copy C-string. " ) ;
memcpy ( ptrw ( ) , p_cstr , len ) ;
2019-03-12 13:57:22 +01:00
}
2020-07-27 12:43:20 +02:00
/*************************************************************************/
/* String */
/*************************************************************************/
//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 ;
}
2021-04-26 09:33:28 +02:00
Error String : : parse_url ( String & r_scheme , String & r_host , int & r_port , String & r_path ) const {
// Splits the URL into scheme, host, port, path. Strip credentials when present.
String base = * this ;
r_scheme = " " ;
r_host = " " ;
r_port = 0 ;
r_path = " " ;
int pos = base . find ( " :// " ) ;
// Scheme
if ( pos ! = - 1 ) {
r_scheme = base . substr ( 0 , pos + 3 ) . to_lower ( ) ;
base = base . substr ( pos + 3 , base . length ( ) - pos - 3 ) ;
}
pos = base . find ( " / " ) ;
// Path
if ( pos ! = - 1 ) {
r_path = base . substr ( pos , base . length ( ) - pos ) ;
base = base . substr ( 0 , pos ) ;
}
// Host
pos = base . find ( " @ " ) ;
if ( pos ! = - 1 ) {
// Strip credentials
base = base . substr ( pos + 1 , base . length ( ) - pos - 1 ) ;
}
if ( base . begins_with ( " [ " ) ) {
// Literal IPv6
pos = base . rfind ( " ] " ) ;
if ( pos = = - 1 ) {
return ERR_INVALID_PARAMETER ;
}
r_host = base . substr ( 1 , pos - 1 ) ;
base = base . substr ( pos + 1 , base . length ( ) - pos - 1 ) ;
} else {
// Anything else
if ( base . get_slice_count ( " : " ) > 1 ) {
return ERR_INVALID_PARAMETER ;
}
pos = base . rfind ( " : " ) ;
if ( pos = = - 1 ) {
r_host = base ;
base = " " ;
} else {
r_host = base . substr ( 0 , pos ) ;
base = base . substr ( pos , base . length ( ) - pos ) ;
}
}
if ( r_host . is_empty ( ) ) {
return ERR_INVALID_PARAMETER ;
}
r_host = r_host . to_lower ( ) ;
// Port
if ( base . begins_with ( " : " ) ) {
base = base . substr ( 1 , base . length ( ) - 1 ) ;
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 ;
}
2014-02-10 02:10:30 +01:00
void String : : copy_from ( const char * p_cstr ) {
2020-07-27 12:43:20 +02:00
// copy Latin-1 encoded c-string directly
2017-06-25 15:57:06 +02:00
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
2017-03-05 16:44:50 +01:00
int len = 0 ;
const char * ptr = p_cstr ;
2020-05-14 16:41:43 +02:00
while ( * ( ptr + + ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
len + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
resize ( 0 ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
resize ( len + 1 ) ; // include 0
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
char32_t * dst = this - > ptrw ( ) ;
2015-06-30 16:59:00 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len + 1 ; i + + ) {
dst [ i ] = p_cstr [ i ] ;
2014-02-10 02:10:30 +01:00
}
}
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char * p_cstr , const int p_clip_to ) {
// copy Latin-1 encoded c-string directly
2017-06-25 15:57:06 +02:00
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
2017-03-05 16:44:50 +01:00
int len = 0 ;
2020-07-27 12:43:20 +02:00
const char * ptr = p_cstr ;
2020-05-14 16:41:43 +02:00
while ( ( p_clip_to < 0 | | len < p_clip_to ) & & * ( ptr + + ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
len + + ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
resize ( 0 ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
resize ( len + 1 ) ; // include 0
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
char32_t * dst = this - > ptrw ( ) ;
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
for ( int i = 0 ; i < len ; i + + ) {
dst [ i ] = p_cstr [ i ] ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
dst [ len ] = 0 ;
}
void String : : copy_from ( const wchar_t * p_cstr ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
parse_utf16 ( ( const char16_t * ) p_cstr ) ;
# else
// wchar_t is 32-bit, copy directly
copy_from ( ( const char32_t * ) p_cstr ) ;
# endif
}
void String : : copy_from ( const wchar_t * p_cstr , const int p_clip_to ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
parse_utf16 ( ( const char16_t * ) p_cstr , p_clip_to ) ;
# else
// wchar_t is 32-bit, copy directly
copy_from ( ( const char32_t * ) p_cstr , p_clip_to ) ;
# endif
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char32_t & p_char ) {
2014-02-10 02:10:30 +01:00
resize ( 2 ) ;
2020-07-27 12:43:20 +02:00
if ( ( p_char > = 0xd800 & & p_char < = 0xdfff ) | | ( p_char > 0x10ffff ) ) {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( p_char , 16 ) + " . " ) ;
set ( 0 , 0xfffd ) ;
} else {
set ( 0 , p_char ) ;
}
2017-03-05 16:44:50 +01:00
set ( 1 , 0 ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char32_t * p_cstr ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
int len = 0 ;
const char32_t * ptr = p_cstr ;
while ( * ( ptr + + ) ! = 0 ) {
len + + ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
copy_from_unchecked ( p_cstr , len ) ;
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
void String : : copy_from ( const char32_t * p_cstr , const int p_clip_to ) {
if ( ! p_cstr ) {
resize ( 0 ) ;
return ;
}
int len = 0 ;
const char32_t * ptr = p_cstr ;
while ( ( p_clip_to < 0 | | len < p_clip_to ) & & * ( ptr + + ) ! = 0 ) {
len + + ;
}
if ( len = = 0 ) {
resize ( 0 ) ;
return ;
}
copy_from_unchecked ( p_cstr , len ) ;
}
// assumes the following have already been validated:
// p_char != nullptr
// p_length > 0
// p_length <= p_char strlen
void String : : copy_from_unchecked ( const char32_t * p_char , const int p_length ) {
resize ( p_length + 1 ) ;
set ( p_length , 0 ) ;
char32_t * dst = ptrw ( ) ;
for ( int i = 0 ; i < p_length ; i + + ) {
if ( ( p_char [ i ] > = 0xd800 & & p_char [ i ] < = 0xdfff ) | | ( p_char [ i ] > 0x10ffff ) ) {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( p_char [ i ] , 16 ) + " . " ) ;
dst [ i ] = 0xfffd ;
} else {
dst [ i ] = p_char [ i ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
void String : : operator = ( const char * p_str ) {
copy_from ( p_str ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
void String : : operator = ( const char32_t * p_str ) {
copy_from ( p_str ) ;
}
void String : : operator = ( const wchar_t * p_str ) {
copy_from ( p_str ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String String : : operator + ( const String & p_str ) const {
String res = * this ;
res + = p_str ;
2014-02-10 02:10:30 +01:00
return res ;
}
2020-07-27 12:43:20 +02:00
String operator + ( const char * p_chr , const String & p_str ) {
String tmp = p_chr ;
tmp + = p_str ;
return tmp ;
}
String operator + ( const wchar_t * p_chr , const String & p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
String tmp = String : : utf16 ( ( const char16_t * ) p_chr ) ;
# else
// wchar_t is 32-bi
String tmp = ( const char32_t * ) p_chr ;
# endif
tmp + = p_str ;
return tmp ;
}
String operator + ( char32_t p_chr , const String & p_str ) {
return ( String : : chr ( p_chr ) + p_str ) ;
}
2017-03-05 16:44:50 +01:00
String & String : : operator + = ( const String & p_str ) {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
* this = p_str ;
2014-02-10 02:10:30 +01:00
return * this ;
}
2016-03-09 00:00:52 +01:00
2020-12-15 13:04:21 +01:00
if ( p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int from = length ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
resize ( length ( ) + p_str . size ( ) ) ;
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = p_str . get_data ( ) ;
char32_t * dst = ptrw ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
set ( length ( ) , 0 ) ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < p_str . length ( ) ; i + + ) {
2017-03-05 16:44:50 +01:00
dst [ from + i ] = src [ i ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return * this ;
}
2017-03-05 16:44:50 +01:00
String & String : : operator + = ( const char * p_str ) {
2020-05-14 16:41:43 +02:00
if ( ! p_str | | p_str [ 0 ] = = 0 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int src_len = 0 ;
const char * ptr = p_str ;
2020-05-14 16:41:43 +02:00
while ( * ( ptr + + ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
src_len + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int from = length ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
resize ( from + src_len + 1 ) ;
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
char32_t * dst = ptrw ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
set ( length ( ) , 0 ) ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < src_len ; i + + ) {
2017-03-05 16:44:50 +01:00
dst [ from + i ] = p_str [ i ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return * this ;
}
2020-07-27 12:43:20 +02:00
String & String : : operator + = ( const wchar_t * p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
* this + = String : : utf16 ( ( const char16_t * ) p_str ) ;
# else
// wchar_t is 32-bit
* this + = String ( ( const char32_t * ) p_str ) ;
# endif
return * this ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
String & String : : operator + = ( const char32_t * p_str ) {
* this + = String ( p_str ) ;
return * this ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
String & String : : operator + = ( char32_t p_char ) {
resize ( size ( ) ? size ( ) + 1 : 2 ) ;
set ( length ( ) , 0 ) ;
if ( ( p_char > = 0xd800 & & p_char < = 0xdfff ) | | ( p_char > 0x10ffff ) ) {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( p_char , 16 ) + " . " ) ;
set ( length ( ) - 1 , 0xfffd ) ;
} else {
set ( length ( ) - 1 , p_char ) ;
}
return * this ;
}
bool String : : operator = = ( const char * p_str ) const {
// compare Latin-1 encoded c-string
int len = 0 ;
const char * aux = p_str ;
while ( * ( aux + + ) ! = 0 ) {
len + + ;
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( length ( ) ! = len ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
int l = length ( ) ;
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * dst = get_data ( ) ;
// Compare char by char
for ( int i = 0 ; i < l ; i + + ) {
if ( ( char32_t ) p_str [ i ] ! = dst [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool String : : operator = = ( const wchar_t * p_str ) const {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit, parse as UTF-16
return * this = = String : : utf16 ( ( const char16_t * ) p_str ) ;
# else
// wchar_t is 32-bit, compare char by char
return * this = = ( const char32_t * ) p_str ;
# endif
}
bool String : : operator = = ( const char32_t * p_str ) const {
2017-03-05 16:44:50 +01:00
int len = 0 ;
2020-07-27 12:43:20 +02:00
const char32_t * aux = p_str ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
while ( * ( aux + + ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
len + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( length ( ) ! = len ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int l = length ( ) ;
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * dst = get_data ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/* Compare char by char */
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < l ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( p_str [ i ] ! = dst [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool String : : operator = = ( const String & p_str ) const {
if ( length ( ) ! = p_str . length ( ) ) {
return false ;
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2020-07-27 12:43:20 +02:00
return true ;
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
int l = length ( ) ;
const char32_t * src = get_data ( ) ;
const char32_t * dst = p_str . get_data ( ) ;
/* Compare char by char */
for ( int i = 0 ; i < l ; i + + ) {
if ( src [ i ] ! = dst [ i ] ) {
return false ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
return true ;
}
bool String : : operator = = ( const StrRange & p_str_range ) const {
int len = p_str_range . len ;
2020-05-14 16:41:43 +02:00
if ( length ( ) ! = len ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * c_str = p_str_range . c_str ;
const char32_t * dst = & operator [ ] ( 0 ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/* Compare char by char */
2020-07-27 12:43:20 +02:00
for ( int i = 0 ; i < len ; i + + ) {
if ( c_str [ i ] ! = dst [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool operator = = ( const char * p_chr , const String & p_str ) {
return p_str = = p_chr ;
}
bool operator = = ( const wchar_t * p_chr , const String & p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return p_str = = String : : utf16 ( ( const char16_t * ) p_chr ) ;
# else
// wchar_t is 32-bi
return p_str = = String ( ( const char32_t * ) p_chr ) ;
# endif
}
2020-11-05 03:01:55 +01:00
bool operator ! = ( const char * p_chr , const String & p_str ) {
return ! ( p_str = = p_chr ) ;
}
bool operator ! = ( const wchar_t * p_chr , const String & p_str ) {
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return ! ( p_str = = String : : utf16 ( ( const char16_t * ) p_chr ) ) ;
# else
// wchar_t is 32-bi
return ! ( p_str = = String ( ( const char32_t * ) p_chr ) ) ;
# endif
}
2014-02-10 02:10:30 +01:00
bool String : : operator ! = ( const char * p_str ) const {
2017-03-05 16:44:50 +01:00
return ( ! ( * this = = p_str ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
bool String : : operator ! = ( const wchar_t * p_str ) const {
2017-03-05 16:44:50 +01:00
return ( ! ( * this = = p_str ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
bool String : : operator ! = ( const char32_t * p_str ) const {
return ( ! ( * this = = p_str ) ) ;
}
bool String : : operator ! = ( const String & p_str ) const {
return ! ( ( * this = = p_str ) ) ;
}
bool String : : operator < = ( const String & p_str ) const {
2020-11-05 03:01:55 +01:00
return ! ( p_str < * this ) ;
}
bool String : : operator > ( const String & p_str ) const {
return p_str < * this ;
}
bool String : : operator > = ( const String & p_str ) const {
return ! ( * this < p_str ) ;
2020-07-27 12:43:20 +02:00
}
bool String : : operator < ( const char * p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str [ 0 ] = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
return is_str_less ( get_data ( ) , p_str ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
bool String : : operator < ( const wchar_t * p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str [ 0 ] = = 0 ) {
2020-07-27 12:43:20 +02:00
return false ;
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2020-07-27 12:43:20 +02:00
return true ;
}
# ifdef WINDOWS_ENABLED
// wchar_t is 16-bit
return is_str_less ( get_data ( ) , String : : utf16 ( ( const char16_t * ) p_str ) . get_data ( ) ) ;
# else
// wchar_t is 32-bit
return is_str_less ( get_data ( ) , ( const char32_t * ) p_str ) ;
# endif
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
bool String : : operator < ( const char32_t * p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str [ 0 ] = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
return is_str_less ( get_data ( ) , p_str ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
bool String : : operator < ( const String & p_str ) const {
2020-07-27 12:43:20 +02:00
return operator < ( p_str . get_data ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
signed char String : : nocasecmp_to ( const String & p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * that_str = p_str . get_data ( ) ;
const char32_t * this_str = get_data ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( true ) {
2020-05-14 16:41:43 +02:00
if ( * that_str = = 0 & & * this_str = = 0 ) {
2014-02-10 02:10:30 +01:00
return 0 ; //we're equal
2020-05-14 16:41:43 +02:00
} else if ( * this_str = = 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ; //if this is empty, and the other one is not, then we're less.. I think?
2020-05-14 16:41:43 +02:00
} else if ( * that_str = = 0 ) {
2014-02-10 02:10:30 +01:00
return 1 ; //otherwise the other one is smaller..
2020-05-14 16:41:43 +02:00
} else if ( _find_upper ( * this_str ) < _find_upper ( * that_str ) ) { //more than
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
} else if ( _find_upper ( * this_str ) > _find_upper ( * that_str ) ) { //less than
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
this_str + + ;
that_str + + ;
}
}
2016-03-09 00:00:52 +01:00
signed char String : : casecmp_to ( const String & p_str ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) & & p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( p_str . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * that_str = p_str . get_data ( ) ;
const char32_t * this_str = get_data ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( true ) {
2020-05-14 16:41:43 +02:00
if ( * that_str = = 0 & & * this_str = = 0 ) {
2014-02-10 02:10:30 +01:00
return 0 ; //we're equal
2020-05-14 16:41:43 +02:00
} else if ( * this_str = = 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ; //if this is empty, and the other one is not, then we're less.. I think?
2020-05-14 16:41:43 +02:00
} else if ( * that_str = = 0 ) {
2014-02-10 02:10:30 +01:00
return 1 ; //otherwise the other one is smaller..
2020-05-14 16:41:43 +02:00
} else if ( * this_str < * that_str ) { //more than
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
} else if ( * this_str > * that_str ) { //less than
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
this_str + + ;
that_str + + ;
}
}
2017-05-11 21:07:59 +02:00
signed char String : : naturalnocasecmp_to ( const String & p_str ) const {
2020-07-27 12:43:20 +02:00
const char32_t * this_str = get_data ( ) ;
const char32_t * that_str = p_str . get_data ( ) ;
2017-05-11 21:07:59 +02:00
if ( this_str & & that_str ) {
2017-05-29 04:56:40 +02:00
while ( * this_str = = ' . ' | | * that_str = = ' . ' ) {
2020-05-14 16:41:43 +02:00
if ( * this_str + + ! = ' . ' ) {
2017-05-29 04:56:40 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
if ( * that_str + + ! = ' . ' ) {
2017-05-29 04:56:40 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
if ( ! * that_str ) {
2017-05-29 04:56:40 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
if ( ! * this_str ) {
2017-05-29 04:56:40 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-29 04:56:40 +02:00
}
2017-05-11 21:07:59 +02:00
while ( * this_str ) {
2020-05-14 16:41:43 +02:00
if ( ! * that_str ) {
2017-05-11 21:07:59 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
} else if ( IS_DIGIT ( * this_str ) ) {
if ( ! IS_DIGIT ( * that_str ) ) {
2017-05-11 21:07:59 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-11 21:07:59 +02:00
2020-07-03 17:47:24 +02:00
// Keep ptrs to start of numerical sequences
2020-10-07 14:23:44 +02:00
const char32_t * this_substr = this_str ;
const char32_t * that_substr = that_str ;
2017-05-11 21:07:59 +02:00
2020-07-03 17:47:24 +02:00
// Compare lengths of both numerical sequences, ignoring leading zeros
2020-05-14 16:41:43 +02:00
while ( IS_DIGIT ( * this_str ) ) {
2017-05-11 21:07:59 +02:00
this_str + + ;
2020-05-14 16:41:43 +02:00
}
while ( IS_DIGIT ( * that_str ) ) {
2017-05-11 21:07:59 +02:00
that_str + + ;
2020-05-14 16:41:43 +02:00
}
2020-07-03 17:47:24 +02:00
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 + + ;
}
2020-05-14 16:41:43 +02:00
} else if ( IS_DIGIT ( * that_str ) ) {
2017-05-11 21:07:59 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
} else {
if ( _find_upper ( * this_str ) < _find_upper ( * that_str ) ) { //more than
2017-05-11 21:07:59 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
} else if ( _find_upper ( * this_str ) > _find_upper ( * that_str ) ) { //less than
2017-05-11 21:07:59 +02:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-11 21:07:59 +02:00
this_str + + ;
that_str + + ;
}
}
2020-05-14 16:41:43 +02:00
if ( * that_str ) {
2017-05-11 21:07:59 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-05-11 21:07:59 +02:00
}
return 0 ;
}
2020-07-27 12:43:20 +02:00
const char32_t * String : : get_data ( ) const {
static const char32_t zero = 0 ;
return size ( ) ? & operator [ ] ( 0 ) : & zero ;
}
2014-02-10 02:10:30 +01:00
void String : : erase ( int p_pos , int p_chars ) {
2017-03-05 16:44:50 +01:00
* this = left ( p_pos ) + substr ( p_pos + p_chars , length ( ) - ( ( p_pos + p_chars ) ) ) ;
2014-02-10 02:10:30 +01:00
}
String String : : capitalize ( ) const {
2017-11-14 21:32:37 +01:00
String aux = this - > camelcase_to_underscore ( true ) . replace ( " _ " , " " ) . strip_edges ( ) ;
2014-02-10 02:10:30 +01:00
String cap ;
2017-03-05 16:44:50 +01:00
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 ] ) ;
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2017-03-05 16:44:50 +01:00
cap + = " " ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
cap + = slice ;
2014-02-10 02:10:30 +01:00
}
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return cap ;
}
2015-03-23 06:08:07 +01:00
2016-02-17 19:26:22 +01:00
String String : : camelcase_to_underscore ( bool lowercase ) const {
2020-07-27 12:43:20 +02:00
const char32_t * cstr = get_data ( ) ;
2016-02-17 19:26:22 +01:00
String new_string ;
2015-03-23 06:08:07 +01:00
const char A = ' A ' , Z = ' Z ' ;
2016-02-17 19:26:22 +01:00
const char a = ' a ' , z = ' z ' ;
int start_index = 0 ;
2015-03-23 06:08:07 +01:00
2017-08-31 23:30:35 +02:00
for ( int i = 1 ; i < this - > size ( ) ; i + + ) {
2016-02-17 19:26:22 +01:00
bool is_upper = cstr [ i ] > = A & & cstr [ i ] < = Z ;
2017-04-23 23:03:16 +02:00
bool is_number = cstr [ i ] > = ' 0 ' & & cstr [ i ] < = ' 9 ' ;
2016-02-17 19:26:22 +01:00
bool are_next_2_lower = false ;
2018-09-30 06:33:21 +02:00
bool is_next_lower = false ;
bool is_next_number = false ;
2017-03-05 16:44:50 +01:00
bool was_precedent_upper = cstr [ i - 1 ] > = A & & cstr [ i - 1 ] < = Z ;
2017-04-23 23:03:16 +02:00
bool was_precedent_number = cstr [ i - 1 ] > = ' 0 ' & & cstr [ i - 1 ] < = ' 9 ' ;
2015-03-23 06:08:07 +01:00
2017-03-05 16:44:50 +01:00
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 ;
2015-03-23 06:08:07 +01:00
}
2018-09-30 06:33:21 +02:00
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 ' ;
}
2019-02-12 21:10:08 +01:00
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 ;
2018-09-30 06:33:21 +02:00
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 ) ;
2019-02-12 21:10:08 +01:00
bool should_split = cond_a | | cond_b | | cond_c | | can_break_number_letter | | can_break_letter_number ;
2016-02-17 19:26:22 +01:00
if ( should_split ) {
new_string + = this - > substr ( start_index , i - start_index ) + " _ " ;
start_index = i ;
}
}
2015-03-23 06:08:07 +01:00
2016-02-17 19:26:22 +01:00
new_string + = this - > substr ( start_index , this - > size ( ) - start_index ) ;
return lowercase ? new_string . to_lower ( ) : new_string ;
2015-03-23 06:08:07 +01:00
}
2019-06-16 04:45:24 +02:00
String String : : get_with_code_lines ( ) const {
2020-04-04 14:27:44 +02:00
const Vector < String > lines = split ( " \n " ) ;
2019-06-16 04:45:24 +02:00
String ret ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
if ( i > 0 ) {
ret + = " \n " ;
}
2020-04-04 14:27:44 +02:00
ret + = vformat ( " %4d | %s " , i + 1 , lines [ i ] ) ;
2019-06-16 04:45:24 +02:00
}
return ret ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
int String : : get_slice_count ( String p_splitter ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-12-15 13:04:21 +01:00
if ( p_splitter . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int pos = 0 ;
int slices = 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( ( pos = find ( p_splitter , pos ) ) > = 0 ) {
2014-02-10 02:10:30 +01:00
slices + + ;
2017-03-05 16:44:50 +01:00
pos + = p_splitter . length ( ) ;
2014-02-10 02:10:30 +01:00
}
return slices ;
}
String String : : get_slice ( String p_splitter , int p_slice ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) | | p_splitter . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int pos = 0 ;
int prev_pos = 0 ;
2017-01-14 12:26:56 +01:00
//int slices=1;
2020-05-14 16:41:43 +02:00
if ( p_slice < 0 ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
if ( find ( p_splitter ) = = - 1 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int i = 0 ;
while ( true ) {
pos = find ( p_splitter , pos ) ;
2020-05-14 16:41:43 +02:00
if ( pos = = - 1 ) {
2017-03-05 16:44:50 +01:00
pos = length ( ) ; //reached end
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int from = prev_pos ;
2017-01-14 12:26:56 +01:00
//int to=pos;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( p_slice = = i ) {
return substr ( from , pos - from ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( pos = = length ( ) ) { //reached end and no find
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
pos + = p_splitter . length ( ) ;
prev_pos = pos ;
2014-02-10 02:10:30 +01:00
i + + ;
}
return " " ; //no find!
}
2020-07-27 12:43:20 +02:00
String String : : get_slicec ( char32_t p_splitter , int p_slice ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2015-06-29 05:29:49 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-06-29 05:29:49 +02:00
2020-05-14 16:41:43 +02:00
if ( p_slice < 0 ) {
2015-06-29 05:29:49 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2015-06-29 05:29:49 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * c = this - > ptr ( ) ;
2017-03-05 16:44:50 +01:00
int i = 0 ;
int prev = 0 ;
int count = 0 ;
while ( true ) {
if ( c [ i ] = = 0 | | c [ i ] = = p_splitter ) {
if ( p_slice = = count ) {
return substr ( prev , i - prev ) ;
} else if ( c [ i ] = = 0 ) {
2016-12-21 18:20:35 +01:00
return String ( ) ;
2015-06-29 05:29:49 +02:00
} else {
count + + ;
2017-03-05 16:44:50 +01:00
prev = i + 1 ;
2015-06-29 05:29:49 +02:00
}
}
i + + ;
}
}
2014-10-10 00:44:27 +02:00
Vector < String > String : : split_spaces ( ) const {
Vector < String > ret ;
2017-03-05 16:44:50 +01:00
int from = 0 ;
int i = 0 ;
2014-10-10 00:44:27 +02:00
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-01-15 22:40:41 +01:00
return ret ;
2020-05-14 16:41:43 +02:00
}
2018-01-15 22:40:41 +01:00
2017-03-05 16:44:50 +01:00
bool inside = false ;
2014-10-10 00:44:27 +02:00
2017-03-05 16:44:50 +01:00
while ( true ) {
bool empty = operator [ ] ( i ) < 33 ;
2014-10-10 00:44:27 +02:00
2020-05-14 16:41:43 +02:00
if ( i = = 0 ) {
2017-03-05 16:44:50 +01:00
inside = ! empty ;
2020-05-14 16:41:43 +02:00
}
2014-10-10 00:44:27 +02:00
if ( ! empty & & ! inside ) {
2017-03-05 16:44:50 +01:00
inside = true ;
from = i ;
2014-10-10 00:44:27 +02:00
}
if ( empty & & inside ) {
2017-03-05 16:44:50 +01:00
ret . push_back ( substr ( from , i - from ) ) ;
inside = false ;
2014-10-10 00:44:27 +02:00
}
2020-05-14 16:41:43 +02:00
if ( i = = len ) {
2014-10-10 00:44:27 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-10-10 00:44:27 +02:00
i + + ;
}
return ret ;
}
2017-12-12 03:14:38 +01:00
Vector < String > String : : split ( const String & p_splitter , bool p_allow_empty , int p_maxsplit ) const {
2014-02-10 02:10:30 +01:00
Vector < String > ret ;
2017-03-05 16:44:50 +01:00
int from = 0 ;
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
int end = find ( p_splitter , from ) ;
2020-05-14 16:41:43 +02:00
if ( end < 0 ) {
2017-03-05 16:44:50 +01:00
end = len ;
2020-05-14 16:41:43 +02:00
}
2017-12-12 03:14:38 +01:00
if ( p_allow_empty | | ( end > from ) ) {
2020-05-14 16:41:43 +02:00
if ( p_maxsplit < = 0 ) {
2017-12-12 03:14:38 +01:00
ret . push_back ( substr ( from , end - from ) ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-12-12 03:14:38 +01:00
// Put rest of the string and leave cycle.
if ( p_maxsplit = = ret . size ( ) ) {
ret . push_back ( substr ( from , len ) ) ;
break ;
}
// Otherwise, push items until positive limit is reached.
ret . push_back ( substr ( from , end - from ) ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
from = end + p_splitter . length ( ) ;
2014-02-10 02:10:30 +01:00
}
return ret ;
}
2017-12-15 20:23:58 +01:00
Vector < String > String : : rsplit ( const String & p_splitter , bool p_allow_empty , int p_maxsplit ) const {
Vector < String > ret ;
const int len = length ( ) ;
2018-07-17 20:45:29 +02:00
int remaining_len = len ;
2017-12-15 20:23:58 +01:00
while ( true ) {
2018-07-17 20:45:29 +02:00
if ( remaining_len < p_splitter . length ( ) | | ( p_maxsplit > 0 & & p_maxsplit = = ret . size ( ) ) ) {
// no room for another splitter or hit max splits, push what's left and we're done
if ( p_allow_empty | | remaining_len > 0 ) {
ret . push_back ( substr ( 0 , remaining_len ) ) ;
2017-12-15 20:23:58 +01:00
}
2018-07-17 20:45:29 +02:00
break ;
2017-12-15 20:23:58 +01:00
}
2018-07-17 20:45:29 +02:00
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 ) ) ;
2017-12-15 20:23:58 +01:00
break ;
2018-07-17 20:45:29 +02:00
}
int substr_start = left_edge + p_splitter . length ( ) ;
if ( p_allow_empty | | substr_start < remaining_len ) {
ret . push_back ( substr ( substr_start , remaining_len - substr_start ) ) ;
}
2017-12-15 20:23:58 +01:00
2018-07-17 20:45:29 +02:00
remaining_len = left_edge ;
2017-12-15 20:23:58 +01:00
}
2021-03-14 08:21:32 +01:00
ret . reverse ( ) ;
2017-12-15 20:23:58 +01:00
return ret ;
}
2017-03-05 16:44:50 +01:00
Vector < float > String : : split_floats ( const String & p_splitter , bool p_allow_empty ) const {
2014-02-10 02:10:30 +01:00
Vector < float > ret ;
2017-03-05 16:44:50 +01:00
int from = 0 ;
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
int end = find ( p_splitter , from ) ;
2020-05-14 16:41:43 +02:00
if ( end < 0 ) {
2017-03-05 16:44:50 +01:00
end = len ;
2020-05-14 16:41:43 +02:00
}
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_float ( & get_data ( ) [ from ] ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
from = end + p_splitter . length ( ) ;
2014-02-10 02:10:30 +01:00
}
return ret ;
}
2017-03-05 16:44:50 +01:00
Vector < float > String : : split_floats_mk ( const Vector < String > & p_splitters , bool p_allow_empty ) const {
2014-02-10 02:10:30 +01:00
Vector < float > ret ;
2017-03-05 16:44:50 +01:00
int from = 0 ;
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
int idx ;
2017-03-05 16:44:50 +01:00
int end = findmk ( p_splitters , from , & idx ) ;
int spl_len = 1 ;
if ( end < 0 ) {
end = len ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
spl_len = p_splitters [ idx ] . length ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_float ( & get_data ( ) [ from ] ) ) ;
2015-01-03 15:06:53 +01:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
from = end + spl_len ;
2014-02-10 02:10:30 +01:00
}
return ret ;
}
2017-03-05 16:44:50 +01:00
Vector < int > String : : split_ints ( const String & p_splitter , bool p_allow_empty ) const {
2014-02-10 02:10:30 +01:00
Vector < int > ret ;
2017-03-05 16:44:50 +01:00
int from = 0 ;
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
int end = find ( p_splitter , from ) ;
2020-05-14 16:41:43 +02:00
if ( end < 0 ) {
2017-03-05 16:44:50 +01:00
end = len ;
2020-05-14 16:41:43 +02:00
}
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_int ( & get_data ( ) [ from ] , end - from ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
from = end + p_splitter . length ( ) ;
2014-02-10 02:10:30 +01:00
}
return ret ;
}
2017-03-05 16:44:50 +01:00
Vector < int > String : : split_ints_mk ( const Vector < String > & p_splitters , bool p_allow_empty ) const {
2014-02-10 02:10:30 +01:00
Vector < int > ret ;
2017-03-05 16:44:50 +01:00
int from = 0 ;
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
int idx ;
2017-03-05 16:44:50 +01:00
int end = findmk ( p_splitters , from , & idx ) ;
int spl_len = 1 ;
if ( end < 0 ) {
end = len ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
spl_len = p_splitters [ idx ] . length ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( p_allow_empty | | ( end > from ) ) {
2020-07-27 12:43:20 +02:00
ret . push_back ( String : : to_int ( & get_data ( ) [ from ] , end - from ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( end = = len ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
from = end + spl_len ;
2014-02-10 02:10:30 +01:00
}
return ret ;
}
2020-07-27 12:43:20 +02:00
String String : : join ( Vector < String > parts ) const {
2017-10-02 17:01:43 +02:00
String ret ;
for ( int i = 0 ; i < parts . size ( ) ; + + i ) {
if ( i > 0 ) {
ret + = * this ;
}
ret + = parts [ i ] ;
}
return ret ;
}
2020-07-27 12:43:20 +02:00
char32_t String : : char_uppercase ( char32_t p_char ) {
2014-02-10 02:10:30 +01:00
return _find_upper ( p_char ) ;
}
2020-07-27 12:43:20 +02:00
char32_t String : : char_lowercase ( char32_t p_char ) {
2014-02-10 02:10:30 +01:00
return _find_lower ( p_char ) ;
}
String String : : to_upper ( ) const {
2017-03-05 16:44:50 +01:00
String upper = * this ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < upper . size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
const char32_t s = upper [ i ] ;
const char32_t t = _find_upper ( s ) ;
2020-05-14 16:41:43 +02:00
if ( s ! = t ) { // avoid copy on write
2017-12-11 23:38:07 +01:00
upper [ i ] = t ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return upper ;
}
String String : : to_lower ( ) const {
2017-12-11 23:38:07 +01:00
String lower = * this ;
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
for ( int i = 0 ; i < lower . size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
const char32_t s = lower [ i ] ;
const char32_t t = _find_lower ( s ) ;
2020-05-14 16:41:43 +02:00
if ( s ! = t ) { // avoid copy on write
2017-12-11 23:38:07 +01:00
lower [ i ] = t ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
return lower ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
String String : : chr ( char32_t p_char ) {
char32_t c [ 2 ] = { p_char , 0 } ;
2014-02-10 02:10:30 +01:00
return String ( c ) ;
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
String String : : num ( double p_num , int p_decimals ) {
2020-05-20 21:49:39 +02:00
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
2020-07-27 12:43:20 +02:00
if ( Math : : is_inf ( p_num ) ) {
if ( signbit ( p_num ) ) {
return " -inf " ;
} else {
return " inf " ;
}
}
2014-02-10 02:10:30 +01:00
# ifndef NO_USE_STDLIB
2020-05-14 16:41:43 +02:00
if ( p_decimals > 16 ) {
2018-03-06 20:40:29 +01:00
p_decimals = 16 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
char fmt [ 7 ] ;
2017-03-05 16:44:50 +01:00
fmt [ 0 ] = ' % ' ;
fmt [ 1 ] = ' . ' ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( p_decimals < 0 ) {
fmt [ 1 ] = ' l ' ;
fmt [ 2 ] = ' f ' ;
fmt [ 3 ] = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else if ( p_decimals < 10 ) {
fmt [ 2 ] = ' 0 ' + p_decimals ;
fmt [ 3 ] = ' l ' ;
fmt [ 4 ] = ' f ' ;
fmt [ 5 ] = 0 ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
fmt [ 2 ] = ' 0 ' + ( p_decimals / 10 ) ;
fmt [ 3 ] = ' 0 ' + ( p_decimals % 10 ) ;
fmt [ 4 ] = ' l ' ;
fmt [ 5 ] = ' f ' ;
fmt [ 6 ] = 0 ;
2014-02-10 02:10:30 +01:00
}
char buf [ 256 ] ;
2015-11-20 00:19:44 +01:00
# if defined(__GNUC__) || defined(_MSC_VER)
2017-03-05 16:44:50 +01:00
snprintf ( buf , 256 , fmt , p_num ) ;
2014-02-10 02:10:30 +01:00
# else
2017-03-05 16:44:50 +01:00
sprintf ( buf , fmt , p_num ) ;
2014-02-10 02:10:30 +01:00
# endif
2017-03-05 16:44:50 +01:00
buf [ 255 ] = 0 ;
2014-02-10 02:10:30 +01:00
//destroy trailing zeroes
{
2017-03-05 16:44:50 +01:00
bool period = false ;
int z = 0 ;
while ( buf [ z ] ) {
2020-05-14 16:41:43 +02:00
if ( buf [ z ] = = ' . ' ) {
2017-03-05 16:44:50 +01:00
period = true ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
z + + ;
}
if ( period ) {
z - - ;
2017-03-05 16:44:50 +01:00
while ( z > 0 ) {
if ( buf [ z ] = = ' 0 ' ) {
buf [ z ] = 0 ;
} else if ( buf [ z ] = = ' . ' ) {
buf [ z ] = 0 ;
2014-02-10 02:10:30 +01:00
break ;
} else {
break ;
}
z - - ;
}
}
}
return buf ;
# else
String s ;
String sd ;
/* integer part */
2017-03-05 16:44:50 +01:00
bool neg = p_num < 0 ;
p_num = ABS ( p_num ) ;
int intn = ( int ) p_num ;
2014-02-10 02:10:30 +01:00
/* decimal part */
2017-03-05 16:44:50 +01:00
if ( p_decimals > 0 | | ( p_decimals = = - 1 & & ( int ) p_num ! = p_num ) ) {
2020-07-27 12:43:20 +02:00
double dec = p_num - ( double ) ( ( int ) p_num ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int digit = 0 ;
if ( p_decimals > MAX_DIGITS )
p_decimals = MAX_DIGITS ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int dec_int = 0 ;
int dec_max = 0 ;
2014-02-10 02:10:30 +01:00
while ( true ) {
2017-03-05 16:44:50 +01:00
dec * = 10.0 ;
dec_int = dec_int * 10 + ( int ) dec % 10 ;
dec_max = dec_max * 10 + 9 ;
2014-02-10 02:10:30 +01:00
digit + + ;
2017-03-05 16:44:50 +01:00
if ( p_decimals = = - 1 ) {
if ( digit = = MAX_DIGITS ) //no point in going to infinite
2014-02-10 02:10:30 +01:00
break ;
2021-01-25 20:46:35 +01:00
if ( dec - ( double ) ( ( int ) dec ) < 1e-6 ) {
2014-02-10 02:10:30 +01:00
break ;
2021-01-25 20:46:35 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( digit = = p_decimals )
2014-02-10 02:10:30 +01:00
break ;
}
2017-03-05 16:44:50 +01:00
dec * = 10 ;
int last = ( int ) dec % 10 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( last > 5 ) {
if ( dec_int = = dec_max ) {
dec_int = 0 ;
2014-02-10 02:10:30 +01:00
intn + + ;
} else {
dec_int + + ;
}
}
String decimal ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < digit ; i + + ) {
char num [ 2 ] = { 0 , 0 } ;
num [ 0 ] = ' 0 ' + dec_int % 10 ;
decimal = num + decimal ;
dec_int / = 10 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
sd = ' . ' + decimal ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( intn = = 0 )
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
s = " 0 " ;
2014-02-10 02:10:30 +01:00
else {
2017-03-05 16:44:50 +01:00
while ( intn ) {
2020-07-27 12:43:20 +02:00
char32_t num = ' 0 ' + ( intn % 10 ) ;
2017-03-05 16:44:50 +01:00
intn / = 10 ;
s = num + s ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
s = s + sd ;
2014-02-10 02:10:30 +01:00
if ( neg )
2017-03-05 16:44:50 +01:00
s = " - " + s ;
2014-02-10 02:10:30 +01:00
return s ;
# endif
}
2015-02-01 19:42:36 +01:00
String String : : num_int64 ( int64_t p_num , int base , bool capitalize_hex ) {
2017-03-05 16:44:50 +01:00
bool sign = p_num < 0 ;
2014-02-10 02:10:30 +01:00
2018-01-13 21:04:30 +01:00
int64_t n = p_num ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int chars = 0 ;
2014-02-10 02:10:30 +01:00
do {
2017-03-05 16:44:50 +01:00
n / = base ;
2014-02-10 02:10:30 +01:00
chars + + ;
2017-03-05 16:44:50 +01:00
} while ( n ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( sign ) {
2014-02-10 02:10:30 +01:00
chars + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String s ;
2017-03-05 16:44:50 +01:00
s . resize ( chars + 1 ) ;
2020-07-27 12:43:20 +02:00
char32_t * c = s . ptrw ( ) ;
2017-03-05 16:44:50 +01:00
c [ chars ] = 0 ;
2018-01-13 21:04:30 +01:00
n = p_num ;
2014-02-10 02:10:30 +01:00
do {
2018-01-13 21:04:30 +01:00
int mod = ABS ( n % base ) ;
2015-02-01 19:42:36 +01:00
if ( mod > = 10 ) {
char a = ( capitalize_hex ? ' A ' : ' a ' ) ;
2017-03-05 16:44:50 +01:00
c [ - - chars ] = a + ( mod - 10 ) ;
2015-02-01 19:42:36 +01:00
} else {
2017-03-05 16:44:50 +01:00
c [ - - chars ] = ' 0 ' + mod ;
2015-02-01 19:42:36 +01:00
}
2017-03-05 16:44:50 +01:00
n / = base ;
} while ( n ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( sign ) {
2017-03-05 16:44:50 +01:00
c [ 0 ] = ' - ' ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return s ;
}
2018-02-22 13:13:51 +01:00
String String : : num_uint64 ( uint64_t p_num , int base , bool capitalize_hex ) {
uint64_t n = p_num ;
int chars = 0 ;
do {
n / = base ;
chars + + ;
} while ( n ) ;
String s ;
s . resize ( chars + 1 ) ;
2020-07-27 12:43:20 +02:00
char32_t * c = s . ptrw ( ) ;
2018-02-22 13:13:51 +01:00
c [ chars ] = 0 ;
n = p_num ;
do {
2018-04-18 22:20:39 +02:00
int mod = n % base ;
2018-02-22 13:13:51 +01:00
if ( mod > = 10 ) {
char a = ( capitalize_hex ? ' A ' : ' a ' ) ;
c [ - - chars ] = a + ( mod - 10 ) ;
} else {
c [ - - chars ] = ' 0 ' + mod ;
}
n / = base ;
} while ( n ) ;
return s ;
}
2014-02-10 02:10:30 +01:00
String String : : num_real ( double p_num ) {
2020-07-27 12:43:20 +02:00
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
if ( Math : : is_inf ( p_num ) ) {
if ( signbit ( p_num ) ) {
return " -inf " ;
} else {
return " inf " ;
}
}
2014-02-10 02:10:30 +01:00
String s ;
String sd ;
/* integer part */
2017-03-05 16:44:50 +01:00
bool neg = p_num < 0 ;
p_num = ABS ( p_num ) ;
int intn = ( int ) p_num ;
2014-02-10 02:10:30 +01:00
/* decimal part */
2017-03-05 16:44:50 +01:00
if ( ( int ) p_num ! = p_num ) {
2020-07-27 12:43:20 +02:00
double dec = p_num - ( double ) ( ( int ) p_num ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int digit = 0 ;
int decimals = MAX_DIGITS ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int dec_int = 0 ;
int dec_max = 0 ;
2014-02-10 02:10:30 +01:00
while ( true ) {
2017-03-05 16:44:50 +01:00
dec * = 10.0 ;
dec_int = dec_int * 10 + ( int ) dec % 10 ;
dec_max = dec_max * 10 + 9 ;
2014-02-10 02:10:30 +01:00
digit + + ;
2020-07-27 12:43:20 +02:00
if ( ( dec - ( double ) ( ( int ) dec ) ) < 1e-6 ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( digit = = decimals ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
dec * = 10 ;
int last = ( int ) dec % 10 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( last > 5 ) {
if ( dec_int = = dec_max ) {
dec_int = 0 ;
2014-02-10 02:10:30 +01:00
intn + + ;
} else {
dec_int + + ;
}
}
String decimal ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < digit ; i + + ) {
char num [ 2 ] = { 0 , 0 } ;
num [ 0 ] = ' 0 ' + dec_int % 10 ;
decimal = num + decimal ;
dec_int / = 10 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
sd = ' . ' + decimal ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
sd = " .0 " ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( intn = = 0 ) {
2017-03-05 16:44:50 +01:00
s = " 0 " ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
while ( intn ) {
2020-07-27 12:43:20 +02:00
char32_t num = ' 0 ' + ( intn % 10 ) ;
2017-03-05 16:44:50 +01:00
intn / = 10 ;
s = num + s ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
s = s + sd ;
2020-05-14 16:41:43 +02:00
if ( neg ) {
2017-03-05 16:44:50 +01:00
s = " - " + s ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return s ;
}
String String : : num_scientific ( double p_num ) {
2020-05-20 21:49:39 +02:00
if ( Math : : is_nan ( p_num ) ) {
return " nan " ;
}
2020-07-27 12:43:20 +02:00
if ( Math : : is_inf ( p_num ) ) {
if ( signbit ( p_num ) ) {
return " -inf " ;
} else {
return " inf " ;
}
}
2014-02-10 02:10:30 +01:00
# ifndef NO_USE_STDLIB
char buf [ 256 ] ;
2015-11-20 00:19:44 +01:00
# if defined(__GNUC__) || defined(_MSC_VER)
2019-11-05 22:04:04 +01:00
2019-11-08 14:04:35 +01:00
# if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
2019-11-05 22:04:04 +01:00
// MinGW and old MSC require _set_output_format() to conform to C99 output for printf
unsigned int old_exponent_format = _set_output_format ( _TWO_DIGIT_EXPONENT ) ;
# endif
2017-03-05 16:44:50 +01:00
snprintf ( buf , 256 , " %lg " , p_num ) ;
2019-11-05 22:04:04 +01:00
2019-11-08 14:04:35 +01:00
# if (defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER < 1900)) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
2019-11-05 22:04:04 +01:00
_set_output_format ( old_exponent_format ) ;
# endif
2014-02-10 02:10:30 +01:00
# else
2017-03-05 16:44:50 +01:00
sprintf ( buf , " %.16lg " , p_num ) ;
2014-02-10 02:10:30 +01:00
# endif
2017-03-05 16:44:50 +01:00
buf [ 255 ] = 0 ;
2014-02-10 02:10:30 +01:00
return buf ;
# else
return String : : num ( p_num ) ;
# endif
}
2020-07-27 12:43:20 +02:00
String String : : md5 ( const uint8_t * p_md5 ) {
return String : : hex_encode_buffer ( p_md5 , 16 ) ;
}
String String : : hex_encode_buffer ( const uint8_t * p_buffer , int p_len ) {
static const char hex [ 16 ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' , ' a ' , ' b ' , ' c ' , ' d ' , ' e ' , ' f ' } ;
String ret ;
char v [ 2 ] = { 0 , 0 } ;
for ( int i = 0 ; i < p_len ; i + + ) {
v [ 0 ] = hex [ p_buffer [ i ] > > 4 ] ;
ret + = v ;
v [ 0 ] = hex [ p_buffer [ i ] & 0xF ] ;
ret + = v ;
}
return ret ;
}
2014-02-10 02:10:30 +01:00
CharString String : : ascii ( bool p_allow_extended ) const {
2020-05-14 16:41:43 +02:00
if ( ! length ( ) ) {
2014-02-10 02:10:30 +01:00
return CharString ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
CharString cs ;
cs . resize ( size ( ) ) ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
if ( ( c < = 0x7f ) | | ( c < = 0xff & & p_allow_extended ) ) {
cs [ i ] = c ;
} else {
print_error ( " Unicode parsing error: Cannot represent " + num_int64 ( c , 16 ) + " as ASCII/Latin-1 character. " ) ;
cs [ i ] = 0x20 ;
}
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return cs ;
}
2017-03-05 16:44:50 +01:00
String String : : utf8 ( const char * p_utf8 , int p_len ) {
2014-02-10 02:10:30 +01:00
String ret ;
2017-03-05 16:44:50 +01:00
ret . parse_utf8 ( p_utf8 , p_len ) ;
2014-02-10 02:10:30 +01:00
return ret ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
bool String : : parse_utf8 ( const char * p_utf8 , int p_len ) {
2020-07-27 12:43:20 +02:00
# define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?");
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! p_utf8 ) {
2019-02-22 19:27:13 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2019-02-22 19:27:13 +01:00
2014-02-10 02:10:30 +01:00
String aux ;
2017-03-05 16:44:50 +01:00
int cstr_size = 0 ;
int str_size = 0 ;
2014-02-10 02:10:30 +01:00
/* HANDLE BOM (Byte Order Mark) */
2017-03-05 16:44:50 +01:00
if ( p_len < 0 | | p_len > = 3 ) {
2020-07-27 12:43:20 +02:00
bool has_bom = uint8_t ( p_utf8 [ 0 ] ) = = 0xef & & uint8_t ( p_utf8 [ 1 ] ) = = 0xbb & & uint8_t ( p_utf8 [ 2 ] ) = = 0xbf ;
2014-02-10 02:10:30 +01:00
if ( has_bom ) {
2020-07-27 12:43:20 +02:00
//8-bit encoding, byte order has no meaning in UTF-8, just skip it
2020-05-14 16:41:43 +02:00
if ( p_len > = 0 ) {
2017-03-05 16:44:50 +01:00
p_len - = 3 ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
p_utf8 + = 3 ;
2014-02-10 02:10:30 +01:00
}
}
{
2017-03-05 16:44:50 +01:00
const char * ptrtmp = p_utf8 ;
const char * ptrtmp_limit = & p_utf8 [ p_len ] ;
int skip = 0 ;
while ( ptrtmp ! = ptrtmp_limit & & * ptrtmp ) {
if ( skip = = 0 ) {
2019-11-20 16:22:16 +01:00
uint8_t c = * ptrtmp > = 0 ? * ptrtmp : uint8_t ( 256 + * ptrtmp ) ;
2014-02-10 02:10:30 +01:00
/* Determine the number of characters in sequence */
2020-05-14 16:41:43 +02:00
if ( ( c & 0x80 ) = = 0 ) {
2017-03-05 16:44:50 +01:00
skip = 0 ;
2020-07-27 12:43:20 +02:00
} else if ( ( c & 0xe0 ) = = 0xc0 ) {
2017-03-05 16:44:50 +01:00
skip = 1 ;
2020-07-27 12:43:20 +02:00
} else if ( ( c & 0xf0 ) = = 0xe0 ) {
2017-03-05 16:44:50 +01:00
skip = 2 ;
2020-07-27 12:43:20 +02:00
} else if ( ( c & 0xf8 ) = = 0xf0 ) {
2017-03-05 16:44:50 +01:00
skip = 3 ;
2020-05-14 16:41:43 +02:00
} else {
2020-07-27 12:43:20 +02:00
_UNICERROR ( " invalid skip at " + num_int64 ( cstr_size ) ) ;
2014-02-10 02:10:30 +01:00
return true ; //invalid utf8
}
2020-07-27 12:43:20 +02:00
if ( skip = = 1 & & ( c & 0x1e ) = = 0 ) {
_UNICERROR ( " overlong rejected at " + num_int64 ( cstr_size ) ) ;
2014-02-10 02:10:30 +01:00
return true ; //reject overlong
}
str_size + + ;
} else {
- - skip ;
}
cstr_size + + ;
ptrtmp + + ;
}
if ( skip ) {
_UNICERROR ( " no space left " ) ;
2021-03-12 14:35:16 +01:00
return true ; //not enough space
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( str_size = = 0 ) {
2014-02-10 02:10:30 +01:00
clear ( ) ;
return false ;
}
2017-03-05 16:44:50 +01:00
resize ( str_size + 1 ) ;
2020-07-27 12:43:20 +02:00
char32_t * dst = ptrw ( ) ;
2017-03-05 16:44:50 +01:00
dst [ str_size ] = 0 ;
2014-02-10 02:10:30 +01:00
while ( cstr_size ) {
2017-03-05 16:44:50 +01:00
int len = 0 ;
/* Determine the number of characters in sequence */
2020-05-14 16:41:43 +02:00
if ( ( * p_utf8 & 0x80 ) = = 0 ) {
2017-03-05 16:44:50 +01:00
len = 1 ;
2020-07-27 12:43:20 +02:00
} else if ( ( * p_utf8 & 0xe0 ) = = 0xc0 ) {
2017-03-05 16:44:50 +01:00
len = 2 ;
2020-07-27 12:43:20 +02:00
} else if ( ( * p_utf8 & 0xf0 ) = = 0xe0 ) {
2017-03-05 16:44:50 +01:00
len = 3 ;
2020-07-27 12:43:20 +02:00
} else if ( ( * p_utf8 & 0xf8 ) = = 0xf0 ) {
2017-03-05 16:44:50 +01:00
len = 4 ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
_UNICERROR ( " invalid len " ) ;
return true ; //invalid UTF8
}
2017-03-05 16:44:50 +01:00
if ( len > cstr_size ) {
2014-02-10 02:10:30 +01:00
_UNICERROR ( " no space left " ) ;
return true ; //not enough space
}
2017-03-05 16:44:50 +01:00
if ( len = = 2 & & ( * p_utf8 & 0x1E ) = = 0 ) {
2014-02-10 02:10:30 +01:00
_UNICERROR ( " no space left " ) ;
return true ; //reject overlong
}
/* Convert the first character */
2017-03-05 16:44:50 +01:00
uint32_t unichar = 0 ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( len = = 1 ) {
2017-03-05 16:44:50 +01:00
unichar = * p_utf8 ;
2020-05-14 16:41:43 +02:00
} else {
2020-07-27 12:43:20 +02:00
unichar = ( 0xff > > ( len + 1 ) ) & * p_utf8 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 1 ; i < len ; i + + ) {
2020-07-27 12:43:20 +02:00
if ( ( p_utf8 [ i ] & 0xc0 ) ! = 0x80 ) {
2014-02-10 02:10:30 +01:00
_UNICERROR ( " invalid utf8 " ) ;
return true ; //invalid utf8
}
2020-07-27 12:43:20 +02:00
if ( unichar = = 0 & & i = = 2 & & ( ( p_utf8 [ i ] & 0x7f ) > > ( 7 - len ) ) = = 0 ) {
2014-02-10 02:10:30 +01:00
_UNICERROR ( " invalid utf8 overlong " ) ;
return true ; //no overlong
}
2020-07-27 12:43:20 +02:00
unichar = ( unichar < < 6 ) | ( p_utf8 [ i ] & 0x3f ) ;
2014-02-10 02:10:30 +01:00
}
}
2020-07-27 12:43:20 +02:00
if ( unichar > = 0xd800 & & unichar < = 0xdfff ) {
_UNICERROR ( " invalid code point " ) ;
return CharString ( ) ;
2014-02-10 02:10:30 +01:00
}
* ( dst + + ) = unichar ;
2017-03-05 16:44:50 +01:00
cstr_size - = len ;
2014-02-10 02:10:30 +01:00
p_utf8 + = len ;
}
return false ;
2020-07-27 12:43:20 +02:00
# undef _UNICERROR
2014-02-10 02:10:30 +01:00
}
CharString String : : utf8 ( ) const {
int l = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! l ) {
2014-02-10 02:10:30 +01:00
return CharString ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * d = & operator [ ] ( 0 ) ;
2017-03-05 16:44:50 +01:00
int fl = 0 ;
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
2020-05-14 16:41:43 +02:00
if ( c < = 0x7f ) { // 7 bits.
2017-03-05 16:44:50 +01:00
fl + = 1 ;
2020-05-14 16:41:43 +02:00
} else if ( c < = 0x7ff ) { // 11 bits
2017-03-05 16:44:50 +01:00
fl + = 2 ;
2014-02-10 02:10:30 +01:00
} else if ( c < = 0xffff ) { // 16 bits
2017-03-05 16:44:50 +01:00
fl + = 3 ;
2020-07-27 12:43:20 +02:00
} else if ( c < = 0x0010ffff ) { // 21 bits
2017-03-05 16:44:50 +01:00
fl + = 4 ;
2020-07-27 12:43:20 +02:00
} else {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( c , 16 ) + " . " ) ;
return CharString ( ) ;
}
if ( c > = 0xd800 & & c < = 0xdfff ) {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( c , 16 ) + " . " ) ;
return CharString ( ) ;
2014-02-10 02:10:30 +01:00
}
}
CharString utf8s ;
2017-03-05 16:44:50 +01:00
if ( fl = = 0 ) {
2014-02-10 02:10:30 +01:00
return utf8s ;
}
2017-03-05 16:44:50 +01:00
utf8s . resize ( fl + 1 ) ;
uint8_t * cdst = ( uint8_t * ) utf8s . get_data ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
# define APPEND_CHAR(m_c) *(cdst++) = m_c
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( c < = 0x7f ) { // 7 bits.
2017-03-05 16:44:50 +01:00
APPEND_CHAR ( c ) ;
2020-05-14 16:41:43 +02:00
} else if ( c < = 0x7ff ) { // 11 bits
2017-03-05 16:44:50 +01:00
APPEND_CHAR ( uint32_t ( 0xc0 | ( ( c > > 6 ) & 0x1f ) ) ) ; // Top 5 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
2014-02-10 02:10:30 +01:00
} else if ( c < = 0xffff ) { // 16 bits
2017-03-05 16:44:50 +01:00
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.
2020-07-27 12:43:20 +02:00
} else { // 21 bits
2017-03-05 16:44:50 +01:00
APPEND_CHAR ( uint32_t ( 0xf0 | ( ( c > > 18 ) & 0x07 ) ) ) ; // Top 3 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 12 ) & 0x3f ) ) ) ; // Upper middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( ( c > > 6 ) & 0x3f ) ) ) ; // Lower middle 6 bits.
APPEND_CHAR ( uint32_t ( 0x80 | ( c & 0x3f ) ) ) ; // Bottom 6 bits.
2014-02-10 02:10:30 +01:00
}
}
# undef APPEND_CHAR
2017-03-05 16:44:50 +01:00
* cdst = 0 ; //trailing zero
2014-02-10 02:10:30 +01:00
return utf8s ;
}
2020-07-27 12:43:20 +02:00
String String : : utf16 ( const char16_t * p_utf16 , int p_len ) {
String ret ;
ret . parse_utf16 ( p_utf16 , p_len ) ;
return ret ;
}
bool String : : parse_utf16 ( const char16_t * p_utf16 , int p_len ) {
# define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?");
if ( ! p_utf16 ) {
return true ;
}
String aux ;
int cstr_size = 0 ;
int str_size = 0 ;
/* HANDLE BOM (Byte Order Mark) */
bool byteswap = false ; // assume correct endianness if no BOM found
if ( p_len < 0 | | p_len > = 1 ) {
bool has_bom = false ;
if ( uint16_t ( p_utf16 [ 0 ] ) = = 0xfeff ) { // correct BOM, read as is
has_bom = true ;
byteswap = false ;
} else if ( uint16_t ( p_utf16 [ 0 ] ) = = 0xfffe ) { // backwards BOM, swap bytes
has_bom = true ;
byteswap = true ;
}
if ( has_bom ) {
if ( p_len > = 0 ) {
p_len - = 1 ;
}
p_utf16 + = 1 ;
}
}
{
const char16_t * ptrtmp = p_utf16 ;
const char16_t * ptrtmp_limit = & p_utf16 [ p_len ] ;
int skip = 0 ;
while ( ptrtmp ! = ptrtmp_limit & & * ptrtmp ) {
uint32_t c = ( byteswap ) ? BSWAP16 ( * ptrtmp ) : * ptrtmp ;
if ( skip = = 0 ) {
if ( ( c & 0xfffffc00 ) = = 0xd800 ) {
skip = 1 ; // lead surrogate
} else if ( ( c & 0xfffffc00 ) = = 0xdc00 ) {
_UNICERROR ( " invalid utf16 surrogate at " + num_int64 ( cstr_size ) ) ;
return true ; // invalid UTF16
} else {
skip = 0 ;
}
str_size + + ;
} else {
if ( ( c & 0xfffffc00 ) = = 0xdc00 ) { // trail surrogate
- - skip ;
} else {
_UNICERROR ( " invalid utf16 surrogate at " + num_int64 ( cstr_size ) ) ;
return true ; // invalid UTF16
}
}
cstr_size + + ;
ptrtmp + + ;
}
if ( skip ) {
_UNICERROR ( " no space left " ) ;
return true ; // not enough space
}
}
if ( str_size = = 0 ) {
clear ( ) ;
return false ;
}
resize ( str_size + 1 ) ;
char32_t * dst = ptrw ( ) ;
dst [ str_size ] = 0 ;
while ( cstr_size ) {
int len = 0 ;
uint32_t c = ( byteswap ) ? BSWAP16 ( * p_utf16 ) : * p_utf16 ;
if ( ( c & 0xfffffc00 ) = = 0xd800 ) {
len = 2 ;
} else {
len = 1 ;
}
if ( len > cstr_size ) {
_UNICERROR ( " no space left " ) ;
return true ; //not enough space
}
uint32_t unichar = 0 ;
if ( len = = 1 ) {
unichar = c ;
} else {
uint32_t c2 = ( byteswap ) ? BSWAP16 ( p_utf16 [ 1 ] ) : p_utf16 [ 1 ] ;
unichar = ( c < < 10UL ) + c2 - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ;
}
* ( dst + + ) = unichar ;
cstr_size - = len ;
p_utf16 + = len ;
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
return false ;
# undef _UNICERROR
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2020-07-27 12:43:20 +02:00
Char16String String : : utf16 ( ) const {
int l = length ( ) ;
if ( ! l ) {
return Char16String ( ) ;
}
const char32_t * d = & operator [ ] ( 0 ) ;
int fl = 0 ;
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
if ( c < = 0xffff ) { // 16 bits.
fl + = 1 ;
} else if ( c < = 0x10ffff ) { // 32 bits.
fl + = 2 ;
} else {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( c , 16 ) + " . " ) ;
return Char16String ( ) ;
}
if ( c > = 0xd800 & & c < = 0xdfff ) {
print_error ( " Unicode parsing error: Invalid unicode codepoint " + num_int64 ( c , 16 ) + " . " ) ;
return Char16String ( ) ;
}
}
Char16String utf16s ;
if ( fl = = 0 ) {
return utf16s ;
}
utf16s . resize ( fl + 1 ) ;
uint16_t * cdst = ( uint16_t * ) utf16s . get_data ( ) ;
# define APPEND_CHAR(m_c) *(cdst++) = m_c
for ( int i = 0 ; i < l ; i + + ) {
uint32_t c = d [ i ] ;
if ( c < = 0xffff ) { // 16 bits.
APPEND_CHAR ( c ) ;
} else { // 32 bits.
APPEND_CHAR ( uint32_t ( ( c > > 10 ) + 0xd7c0 ) ) ; // lead surrogate.
APPEND_CHAR ( uint32_t ( ( c & 0x3ff ) | 0xdc00 ) ) ; // trail surrogate.
}
}
# undef APPEND_CHAR
* cdst = 0 ; //trailing zero
2020-05-19 15:46:49 +02:00
2020-07-27 12:43:20 +02:00
return utf16s ;
}
2014-02-10 02:10:30 +01:00
String : : String ( const char * p_str ) {
copy_from ( p_str ) ;
}
2018-07-25 03:11:03 +02:00
2020-07-27 12:43:20 +02:00
String : : String ( const wchar_t * p_str ) {
copy_from ( p_str ) ;
}
String : : String ( const char32_t * p_str ) {
copy_from ( p_str ) ;
}
String : : String ( const char * p_str , int p_clip_to_len ) {
copy_from ( p_str , p_clip_to_len ) ;
}
String : : String ( const wchar_t * p_str , int p_clip_to_len ) {
copy_from ( p_str , p_clip_to_len ) ;
}
String : : String ( const char32_t * p_str , int p_clip_to_len ) {
2017-03-05 16:44:50 +01:00
copy_from ( p_str , p_clip_to_len ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String : : String ( const StrRange & p_range ) {
2020-05-14 16:41:43 +02:00
if ( ! p_range . c_str ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
copy_from ( p_range . c_str , p_range . len ) ;
2014-02-10 02:10:30 +01:00
}
2021-01-28 13:39:05 +01:00
int64_t String : : hex_to_int ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
2017-01-09 00:58:39 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * s = ptr ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int64_t sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( sign < 0 ) {
2017-01-09 00:58:39 +01:00
s + + ;
}
2021-01-28 13:39:05 +01:00
if ( len > 2 & & s [ 0 ] = = ' 0 ' & & s [ 1 ] = = ' x ' ) {
2017-03-05 16:44:50 +01:00
s + = 2 ;
2018-04-18 22:20:39 +02:00
}
2017-01-09 00:58:39 +01:00
2017-03-05 16:44:50 +01:00
int64_t hex = 0 ;
2017-01-09 00:58:39 +01:00
2017-03-05 16:44:50 +01:00
while ( * s ) {
2020-07-27 12:43:20 +02:00
char32_t c = LOWERCASE ( * s ) ;
2017-01-09 00:58:39 +01:00
int64_t n ;
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
n = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
n = ( c - ' a ' ) + 10 ;
2017-01-09 00:58:39 +01:00
} else {
return 0 ;
}
2020-07-01 09:13:12 +02:00
// Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
bool overflow = ( ( 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. " ) ) ;
2017-03-05 16:44:50 +01:00
hex * = 16 ;
hex + = n ;
2017-01-09 00:58:39 +01:00
s + + ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return hex * sign ;
2014-02-10 02:10:30 +01:00
}
2021-01-28 13:39:05 +01:00
int64_t String : : bin_to_int ( ) const {
int len = length ( ) ;
if ( len = = 0 ) {
2019-04-25 14:43:48 +02:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2019-04-25 14:43:48 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * s = ptr ( ) ;
2019-04-25 14:43:48 +02:00
int64_t sign = s [ 0 ] = = ' - ' ? - 1 : 1 ;
if ( sign < 0 ) {
s + + ;
}
2021-01-28 13:39:05 +01:00
if ( len > 2 & & s [ 0 ] = = ' 0 ' & & s [ 1 ] = = ' b ' ) {
2019-04-25 14:43:48 +02:00
s + = 2 ;
}
int64_t binary = 0 ;
while ( * s ) {
2020-07-27 12:43:20 +02:00
char32_t c = LOWERCASE ( * s ) ;
2019-04-25 14:43:48 +02:00
int64_t n ;
if ( c = = ' 0 ' | | c = = ' 1 ' ) {
n = c - ' 0 ' ;
} else {
return 0 ;
}
2020-07-01 09:13:12 +02:00
// Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
bool overflow = ( ( binary > INT64_MAX / 2 ) & & ( sign = = 1 | | ( sign = = - 1 & & binary ! = ( INT64_MAX > > 1 ) + 1 ) ) ) | | ( sign = = - 1 & & binary = = ( INT64_MAX > > 1 ) + 1 & & c > ' 0 ' ) ;
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. " ) ) ;
2019-04-25 14:43:48 +02:00
binary * = 2 ;
binary + = n ;
s + + ;
}
return binary * sign ;
}
2020-05-13 11:31:51 +02:00
int64_t String : : to_int ( ) const {
2020-05-14 16:41:43 +02:00
if ( length ( ) = = 0 ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int to = ( find ( " . " ) > = 0 ) ? find ( " . " ) : length ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int64_t integer = 0 ;
int64_t sign = 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < to ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
2020-07-01 09:13:12 +02:00
bool overflow = ( integer > INT64_MAX / 10 ) | | ( integer = = INT64_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
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. " ) ) ;
2017-03-05 16:44:50 +01:00
integer * = 10 ;
integer + = c - ' 0 ' ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else if ( integer = = 0 & & c = = ' - ' ) {
sign = - sign ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
return integer * sign ;
2014-02-10 02:10:30 +01:00
}
2020-06-03 06:04:04 +02:00
int64_t String : : to_int ( const char * p_str , int p_len ) {
2017-03-05 16:44:50 +01:00
int to = 0 ;
2020-05-14 16:41:43 +02:00
if ( p_len > = 0 ) {
2017-03-05 16:44:50 +01:00
to = p_len ;
2020-05-14 16:41:43 +02:00
} else {
while ( p_str [ to ] ! = 0 & & p_str [ to ] ! = ' . ' ) {
2016-01-03 21:14:28 +01:00
to + + ;
2020-05-14 16:41:43 +02:00
}
2016-01-03 21:14:28 +01:00
}
2014-02-10 02:10:30 +01:00
2020-06-03 06:04:04 +02:00
int64_t integer = 0 ;
int64_t sign = 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < to ; i + + ) {
char c = p_str [ i ] ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
2020-07-01 09:13:12 +02:00
bool overflow = ( integer > INT64_MAX / 10 ) | | ( integer = = INT64_MAX / 10 & & ( ( sign = = 1 & & c > ' 7 ' ) | | ( sign = = - 1 & & c > ' 8 ' ) ) ) ;
ERR_FAIL_COND_V_MSG ( overflow , sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + String ( p_str ) . substr ( 0 , to ) + " as integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
2017-03-05 16:44:50 +01:00
integer * = 10 ;
integer + = c - ' 0 ' ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else if ( c = = ' - ' & & integer = = 0 ) {
sign = - sign ;
2020-05-14 16:41:43 +02:00
} else if ( c ! = ' ' ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
return integer * sign ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
int64_t String : : to_int ( const wchar_t * p_str , int p_len ) {
int to = 0 ;
if ( p_len > = 0 ) {
to = p_len ;
} else {
while ( p_str [ to ] ! = 0 & & p_str [ to ] ! = ' . ' ) {
to + + ;
}
}
int64_t integer = 0 ;
int64_t sign = 1 ;
for ( int i = 0 ; i < to ; i + + ) {
wchar_t c = p_str [ i ] ;
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 " + 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 ;
}
2014-02-10 02:10:30 +01:00
bool String : : is_numeric ( ) const {
if ( length ( ) = = 0 ) {
return false ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
int s = 0 ;
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( 0 ) = = ' - ' ) {
2020-05-10 12:56:01 +02:00
+ + s ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
bool dot = false ;
2017-03-05 16:44:50 +01:00
for ( int i = s ; i < length ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2014-02-10 02:10:30 +01:00
if ( c = = ' . ' ) {
if ( dot ) {
return false ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
dot = true ;
2020-07-27 12:43:20 +02:00
} else if ( c < ' 0 ' | | c > ' 9 ' ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-19 15:46:49 +02:00
}
}
2014-02-10 02:10:30 +01:00
return true ; // TODO: Use the parser below for this instead
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
template < class C >
static double built_in_strtod ( const C * string , /* A decimal ASCII floating-point number,
2014-02-10 02:10:30 +01:00
* 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 ) . */
2020-04-02 01:20:12 +02:00
C * * endPtr = nullptr ) /* If non-nullptr, store terminating Cacter's
2014-02-10 02:10:30 +01:00
* address here . */
{
2017-03-05 16:44:50 +01:00
static const int maxExponent = 511 ; /* Largest possible base 10 exponent. Any
2014-02-10 02:10:30 +01:00
* exponent larger than this will already
* produce underflow or overflow , so there ' s
* no need to worry about additional digits .
*/
2017-03-05 16:44:50 +01:00
static const double powersOf10 [ ] = { /* Table giving binary powers of 10. Entry */
10. , /* is 10^2^i. Used to convert decimal */
100. , /* exponents into floating-point numbers. */
1.0e4 ,
1.0e8 ,
1.0e16 ,
1.0e32 ,
1.0e64 ,
1.0e128 ,
1.0e256
2014-02-10 02:10:30 +01:00
} ;
2019-12-10 05:13:02 +01:00
bool sign , expSign = false ;
2017-03-05 16:44:50 +01:00
double fraction , dblExp ;
const double * d ;
2018-10-03 14:05:17 +02:00
const C * p ;
int c ;
2017-03-05 16:44:50 +01:00
int exp = 0 ; /* Exponent read from "EX" field. */
int fracExp = 0 ; /* Exponent that derives from the fractional
2017-09-22 05:58:29 +02:00
* part . Under normal circumstances , it is
2014-02-10 02:10:30 +01:00
* 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 . */
2017-03-05 16:44:50 +01:00
int mantSize ; /* Number of digits in mantissa. */
int decPt ; /* Number of mantissa digits BEFORE decimal
2014-02-10 02:10:30 +01:00
* point . */
2017-03-05 16:44:50 +01:00
const C * pExp ; /* Temporarily holds location of exponent in
2014-02-10 02:10:30 +01:00
* string . */
2017-03-05 16:44:50 +01:00
/*
2014-02-10 02:10:30 +01:00
* Strip off leading blanks and check for a sign .
*/
2017-03-05 16:44:50 +01:00
p = string ;
while ( * p = = ' ' | | * p = = ' \t ' | | * p = = ' \n ' ) {
p + = 1 ;
}
if ( * p = = ' - ' ) {
sign = true ;
p + = 1 ;
} else {
if ( * p = = ' + ' ) {
p + = 1 ;
}
sign = false ;
}
/*
2014-02-10 02:10:30 +01:00
* Count the number of digits in the mantissa ( including the decimal
* point ) , and also locate the decimal point .
*/
2017-03-05 16:44:50 +01:00
decPt = - 1 ;
for ( mantSize = 0 ; ; mantSize + = 1 ) {
c = * p ;
if ( ! IS_DIGIT ( c ) ) {
if ( ( c ! = ' . ' ) | | ( decPt > = 0 ) ) {
break ;
}
decPt = mantSize ;
}
p + = 1 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
/*
2014-02-10 02:10:30 +01:00
* Now suck up the digits in the mantissa . Use two integers to collect 9
* digits each ( this is faster than using floating - point ) . If the mantissa
* has more than 18 digits , ignore the extras , since they can ' t affect the
* value anyway .
*/
2017-03-05 16:44:50 +01:00
pExp = p ;
p - = mantSize ;
if ( decPt < 0 ) {
decPt = mantSize ;
} else {
mantSize - = 1 ; /* One of the digits was the point. */
}
if ( mantSize > 18 ) {
fracExp = decPt - 18 ;
mantSize = 18 ;
} else {
fracExp = decPt - mantSize ;
}
if ( mantSize = = 0 ) {
fraction = 0.0 ;
p = string ;
goto done ;
} else {
int frac1 , frac2 ;
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 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
/*
2014-02-10 02:10:30 +01:00
* Skim off the exponent .
*/
2017-03-05 16:44:50 +01:00
p = pExp ;
if ( ( * p = = ' E ' ) | | ( * p = = ' e ' ) ) {
2014-02-10 02:10:30 +01:00
p + = 1 ;
2017-03-05 16:44:50 +01:00
if ( * p = = ' - ' ) {
expSign = true ;
p + = 1 ;
} else {
if ( * p = = ' + ' ) {
p + = 1 ;
}
expSign = false ;
}
2020-07-27 12:43:20 +02:00
if ( ! IS_DIGIT ( char32_t ( * p ) ) ) {
2017-03-05 16:44:50 +01:00
p = pExp ;
goto done ;
}
2020-07-27 12:43:20 +02:00
while ( IS_DIGIT ( char32_t ( * p ) ) ) {
2017-03-05 16:44:50 +01:00
exp = exp * 10 + ( * p - ' 0 ' ) ;
p + = 1 ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( expSign ) {
exp = fracExp - exp ;
} else {
exp = fracExp + exp ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
/*
2014-02-10 02:10:30 +01:00
* Generate a floating - point number that represents the exponent . Do this
* by processing the exponent one bit at a time to combine many powers of
* 2 of 10. Then combine the exponent with the fraction .
*/
2017-03-05 16:44:50 +01:00
if ( exp < 0 ) {
expSign = true ;
exp = - exp ;
} else {
expSign = false ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
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 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
done :
2020-04-02 01:20:12 +02:00
if ( endPtr ! = nullptr ) {
2017-03-05 16:44:50 +01:00
* endPtr = ( C * ) p ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( sign ) {
return - fraction ;
}
return fraction ;
2014-02-10 02:10:30 +01:00
}
# define READING_SIGN 0
# define READING_INT 1
# define READING_DEC 2
# define READING_EXP 3
# define READING_DONE 4
2020-07-24 20:07:57 +02:00
double String : : to_float ( const char * p_str ) {
2014-02-10 02:10:30 +01:00
return built_in_strtod < char > ( p_str ) ;
}
2020-07-27 12:43:20 +02:00
double String : : to_float ( const char32_t * p_str , const char32_t * * r_end ) {
return built_in_strtod < char32_t > ( p_str , ( char32_t * * ) r_end ) ;
}
double String : : to_float ( const wchar_t * p_str , const wchar_t * * r_end ) {
return built_in_strtod < wchar_t > ( p_str , ( wchar_t * * ) r_end ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-27 12:43:20 +02:00
int64_t String : : to_int ( const char32_t * p_str , int p_len , bool p_clamp ) {
2020-05-14 16:41:43 +02:00
if ( p_len = = 0 | | ! p_str [ 0 ] ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
///@todo make more exact so saving and loading does not lose precision
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int64_t integer = 0 ;
int64_t sign = 1 ;
int reading = READING_SIGN ;
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * str = p_str ;
const char32_t * limit = & p_str [ p_len ] ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( * str & & reading ! = READING_DONE & & str ! = limit ) {
2020-07-27 12:43:20 +02:00
char32_t c = * ( str + + ) ;
2017-03-05 16:44:50 +01:00
switch ( reading ) {
2014-02-10 02:10:30 +01:00
case READING_SIGN : {
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
reading = READING_INT ;
2014-02-10 02:10:30 +01:00
// let it fallthrough
2017-03-05 16:44:50 +01:00
} else if ( c = = ' - ' ) {
sign = - 1 ;
reading = READING_INT ;
2014-02-10 02:10:30 +01:00
break ;
2017-03-05 16:44:50 +01:00
} else if ( c = = ' + ' ) {
sign = 1 ;
reading = READING_INT ;
2014-02-10 02:10:30 +01:00
break ;
} else {
break ;
}
2020-02-22 20:47:50 +01:00
[[fallthrough]] ;
2014-02-10 02:10:30 +01:00
}
case READING_INT : {
2017-03-05 16:44:50 +01:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
2019-09-22 18:45:08 +02:00
if ( integer > INT64_MAX / 10 ) {
2019-08-22 17:22:35 +02:00
String number ( " " ) ;
str = p_str ;
2019-08-24 13:34:14 +02:00
while ( * str & & str ! = limit ) {
2019-08-22 17:22:35 +02:00
number + = * ( str + + ) ;
}
2020-05-01 14:34:23 +02:00
if ( p_clamp ) {
if ( sign = = 1 ) {
return INT64_MAX ;
} else {
return INT64_MIN ;
}
} else {
ERR_FAIL_V_MSG ( sign = = 1 ? INT64_MAX : INT64_MIN , " Cannot represent " + number + " as integer, provided value is " + ( sign = = 1 ? " too big. " : " too small. " ) ) ;
}
2019-08-22 17:22:35 +02:00
}
2017-03-05 16:44:50 +01:00
integer * = 10 ;
integer + = c - ' 0 ' ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
reading = READING_DONE ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
} break ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
return sign * integer ;
2014-02-10 02:10:30 +01:00
}
2020-07-24 20:07:57 +02:00
double String : : to_float ( ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
return built_in_strtod < char32_t > ( get_data ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
uint32_t String : : hash ( const char * p_cstr ) {
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
uint32_t c ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
while ( ( c = * p_cstr + + ) ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
return hashv ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
uint32_t String : : hash ( const char * p_cstr , int p_len ) {
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < p_len ; i + + ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + p_cstr [ i ] ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
2020-07-27 12:43:20 +02:00
uint32_t String : : hash ( const wchar_t * 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 wchar_t * 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 char32_t * p_cstr , int p_len ) {
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < p_len ; i + + ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + p_cstr [ i ] ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
2020-07-27 12:43:20 +02:00
uint32_t String : : hash ( const char32_t * p_cstr ) {
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
uint32_t c ;
2020-05-14 16:41:43 +02:00
while ( ( c = * p_cstr + + ) ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
uint32_t String : : hash ( ) const {
/* simple djb2 hashing */
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * chr = get_data ( ) ;
2014-02-10 02:10:30 +01:00
uint32_t hashv = 5381 ;
uint32_t c ;
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
while ( ( c = * chr + + ) ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return hashv ;
}
uint64_t String : : hash64 ( ) const {
/* simple djb2 hashing */
2020-07-27 12:43:20 +02:00
const char32_t * chr = get_data ( ) ;
2014-02-10 02:10:30 +01:00
uint64_t hashv = 5381 ;
uint64_t c ;
2020-05-14 16:41:43 +02:00
while ( ( c = * chr + + ) ) {
2014-02-10 02:10:30 +01:00
hashv = ( ( hashv < < 5 ) + hashv ) + c ; /* hash * 33 + c */
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return hashv ;
}
2014-03-14 02:57:24 +01:00
String String : : md5_text ( ) const {
2017-03-05 16:44:50 +01:00
CharString cs = utf8 ( ) ;
2019-07-02 03:06:52 +02:00
unsigned char hash [ 16 ] ;
CryptoCore : : md5 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
return String : : hex_encode_buffer ( hash , 16 ) ;
2014-03-14 02:57:24 +01:00
}
2014-02-10 02:10:30 +01:00
2019-07-02 16:07:02 +02:00
String String : : sha1_text ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 20 ] ;
CryptoCore : : sha1 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
return String : : hex_encode_buffer ( hash , 20 ) ;
}
2016-06-17 09:55:16 +02:00
String String : : sha256_text ( ) const {
2017-03-05 16:44:50 +01:00
CharString cs = utf8 ( ) ;
2016-06-17 09:55:16 +02:00
unsigned char hash [ 32 ] ;
2019-07-02 03:06:52 +02:00
CryptoCore : : sha256 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
2016-06-17 09:55:16 +02:00
return String : : hex_encode_buffer ( hash , 32 ) ;
}
2014-08-02 03:10:38 +02:00
Vector < uint8_t > String : : md5_buffer ( ) const {
2017-03-05 16:44:50 +01:00
CharString cs = utf8 ( ) ;
2019-07-02 03:06:52 +02:00
unsigned char hash [ 16 ] ;
CryptoCore : : md5 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
2014-08-02 03:10:38 +02:00
Vector < uint8_t > ret ;
ret . resize ( 16 ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < 16 ; i + + ) {
2019-07-02 03:06:52 +02:00
ret . write [ i ] = hash [ i ] ;
}
2014-08-02 03:10:38 +02:00
return ret ;
2020-05-19 15:46:49 +02:00
}
2014-08-02 03:10:38 +02:00
2019-07-02 16:07:02 +02:00
Vector < uint8_t > String : : sha1_buffer ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 20 ] ;
CryptoCore : : sha1 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
Vector < uint8_t > ret ;
ret . resize ( 20 ) ;
for ( int i = 0 ; i < 20 ; i + + ) {
ret . write [ i ] = hash [ i ] ;
}
return ret ;
}
2016-06-23 18:57:45 +02:00
Vector < uint8_t > String : : sha256_buffer ( ) const {
CharString cs = utf8 ( ) ;
unsigned char hash [ 32 ] ;
2019-07-02 03:06:52 +02:00
CryptoCore : : sha256 ( ( unsigned char * ) cs . ptr ( ) , cs . length ( ) , hash ) ;
2016-06-23 18:57:45 +02:00
Vector < uint8_t > ret ;
ret . resize ( 32 ) ;
for ( int i = 0 ; i < 32 ; i + + ) {
2018-07-25 03:11:03 +02:00
ret . write [ i ] = hash [ i ] ;
2016-06-23 18:57:45 +02:00
}
return ret ;
}
2017-12-11 23:38:07 +01:00
String String : : insert ( int p_at_pos , const String & p_string ) const {
2020-05-14 16:41:43 +02:00
if ( p_at_pos < 0 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( p_at_pos > length ( ) ) {
2017-03-05 16:44:50 +01:00
p_at_pos = length ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String pre ;
2020-05-14 16:41:43 +02:00
if ( p_at_pos > 0 ) {
2017-03-05 16:44:50 +01:00
pre = substr ( 0 , p_at_pos ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String post ;
2020-05-14 16:41:43 +02:00
if ( p_at_pos < length ( ) ) {
2017-03-05 16:44:50 +01:00
post = substr ( p_at_pos , length ( ) - p_at_pos ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return pre + p_string + post ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
String String : : substr ( int p_from , int p_chars ) const {
2020-05-14 16:41:43 +02:00
if ( p_chars = = - 1 ) {
2019-05-03 14:21:04 +02:00
p_chars = length ( ) - p_from ;
2020-05-14 16:41:43 +02:00
}
2019-05-03 14:21:04 +02:00
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) | | p_from < 0 | | p_from > = length ( ) | | p_chars < = 0 ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ( p_from + p_chars ) > length ( ) ) {
p_chars = length ( ) - p_from ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
if ( p_from = = 0 & & p_chars > = length ( ) ) {
return String ( * this ) ;
}
2018-07-24 17:06:49 +02:00
String s = String ( ) ;
2020-07-27 12:43:20 +02:00
s . copy_from_unchecked ( & get_data ( ) [ p_from ] , p_chars ) ;
2018-07-24 17:06:49 +02:00
return s ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
int String : : find ( const String & p_str , int p_from ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
const int src_len = p_str . length ( ) ;
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
const int len = length ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
const char32_t * str = p_str . get_data ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = p_from ; i < = ( len - src_len ) ; i + + ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( read_pos > = len ) {
2014-02-10 02:10:30 +01:00
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2017-12-11 23:38:07 +01:00
if ( src [ read_pos ] ! = str [ j ] ) {
2017-03-05 16:44:50 +01:00
found = false ;
2014-02-10 02:10:30 +01:00
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
2017-12-11 23:38:07 +01:00
int String : : find ( const char * p_str , int p_from ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2017-12-11 23:38:07 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
const int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2017-12-11 23:38:07 +01:00
int src_len = 0 ;
2020-05-14 16:41:43 +02:00
while ( p_str [ src_len ] ! = ' \0 ' ) {
2017-12-11 23:38:07 +01:00
src_len + + ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
if ( src_len = = 1 ) {
2020-07-27 12:43:20 +02:00
const char32_t needle = p_str [ 0 ] ;
2017-12-11 23:38:07 +01:00
for ( int i = p_from ; i < len ; i + + ) {
if ( src [ i ] = = needle ) {
return i ;
}
}
} else {
for ( int i = p_from ; i < = ( len - src_len ) ; i + + ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
if ( read_pos > = len ) {
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2017-12-11 23:38:07 +01:00
2020-07-27 12:43:20 +02:00
if ( src [ read_pos ] ! = ( char32_t ) p_str [ j ] ) {
2017-12-11 23:38:07 +01:00
found = false ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2017-12-11 23:38:07 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
}
}
return - 1 ;
}
2020-07-27 12:43:20 +02:00
int String : : find_char ( const char32_t & p_char , int p_from ) const {
2019-01-07 18:02:55 +01:00
return _cowdata . find ( p_char , p_from ) ;
}
2017-03-05 16:44:50 +01:00
int String : : findmk ( const Vector < String > & p_keys , int p_from , int * r_key ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
if ( p_keys . size ( ) = = 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
//int src_len=p_str.length();
2017-03-05 16:44:50 +01:00
const String * keys = & p_keys [ 0 ] ;
int key_count = p_keys . size ( ) ;
2014-02-10 02:10:30 +01:00
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = p_from ; i < len ; i + + ) {
bool found = true ;
for ( int k = 0 ; k < key_count ; k + + ) {
found = true ;
2020-05-14 16:41:43 +02:00
if ( r_key ) {
2017-03-05 16:44:50 +01:00
* r_key = k ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
const char32_t * cmp = keys [ k ] . get_data ( ) ;
2017-03-05 16:44:50 +01:00
int l = keys [ k ] . length ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < l ; j + + ) {
int read_pos = i + j ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( read_pos > = len ) {
found = false ;
2014-02-10 02:10:30 +01:00
break ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( src [ read_pos ] ! = cmp [ j ] ) {
found = false ;
2014-02-10 02:10:30 +01:00
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
2017-12-11 23:38:07 +01:00
int String : : findn ( const String & p_str , int p_from ) const {
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2014-02-10 02:10:30 +01:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int src_len = p_str . length ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | length ( ) = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * srcd = get_data ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = p_from ; i < = ( length ( ) - src_len ) ; i + + ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( read_pos > = length ( ) ) {
2014-02-10 02:10:30 +01:00
ERR_PRINT ( " read_pos>=length() " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
char32_t src = _find_lower ( srcd [ read_pos ] ) ;
char32_t dst = _find_lower ( p_str [ j ] ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( src ! = dst ) {
found = false ;
2014-02-10 02:10:30 +01:00
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-02-10 02:10:30 +01:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
2017-12-11 23:38:07 +01:00
int String : : rfind ( const String & p_str , int p_from ) const {
2017-09-22 05:58:29 +02:00
// establish a limit
2017-03-05 16:44:50 +01:00
int limit = length ( ) - p_str . length ( ) ;
2020-05-14 16:41:43 +02:00
if ( limit < 0 ) {
2014-09-21 06:43:42 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2017-09-22 05:58:29 +02:00
// establish a starting point
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2017-03-05 16:44:50 +01:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
} else if ( p_from > limit ) {
2017-03-05 16:44:50 +01:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
int src_len = p_str . length ( ) ;
int len = length ( ) ;
2014-09-21 06:43:42 +02:00
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | len = = 0 ) {
2017-09-22 05:58:29 +02:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = p_from ; i > = 0 ; i - - ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
if ( read_pos > = len ) {
2014-09-21 06:43:42 +02:00
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
if ( src [ read_pos ] ! = p_str [ j ] ) {
found = false ;
2014-09-21 06:43:42 +02:00
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-09-21 06:43:42 +02:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
}
2014-02-10 02:10:30 +01:00
return - 1 ;
}
2020-05-14 14:29:06 +02:00
2017-12-11 23:38:07 +01:00
int String : : rfindn ( const String & p_str , int p_from ) const {
2017-09-22 05:58:29 +02:00
// establish a limit
2017-03-05 16:44:50 +01:00
int limit = length ( ) - p_str . length ( ) ;
2020-05-14 16:41:43 +02:00
if ( limit < 0 ) {
2014-09-21 06:43:42 +02:00
return - 1 ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2017-09-22 05:58:29 +02:00
// establish a starting point
2020-05-14 16:41:43 +02:00
if ( p_from < 0 ) {
2017-03-05 16:44:50 +01:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
} else if ( p_from > limit ) {
2017-03-05 16:44:50 +01:00
p_from = limit ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
int src_len = p_str . length ( ) ;
int len = length ( ) ;
2014-09-21 06:43:42 +02:00
2020-05-14 16:41:43 +02:00
if ( src_len = = 0 | | len = = 0 ) {
2018-01-18 21:37:17 +01:00
return - 1 ; // won't find anything!
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
2020-07-27 12:43:20 +02:00
const char32_t * src = get_data ( ) ;
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = p_from ; i > = 0 ; i - - ) {
bool found = true ;
for ( int j = 0 ; j < src_len ; j + + ) {
int read_pos = i + j ;
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
if ( read_pos > = len ) {
2014-09-21 06:43:42 +02:00
ERR_PRINT ( " read_pos>=len " ) ;
return - 1 ;
2020-05-19 15:46:49 +02:00
}
2014-09-21 06:43:42 +02:00
2020-07-27 12:43:20 +02:00
char32_t srcc = _find_lower ( src [ read_pos ] ) ;
char32_t dstc = _find_lower ( p_str [ j ] ) ;
2014-09-21 06:43:42 +02:00
2017-03-05 16:44:50 +01:00
if ( srcc ! = dstc ) {
found = false ;
2014-09-21 06:43:42 +02:00
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( found ) {
2014-09-21 06:43:42 +02:00
return i ;
2020-05-14 16:41:43 +02:00
}
2014-09-21 06:43:42 +02:00
}
2014-02-10 02:10:30 +01:00
return - 1 ;
}
2017-03-05 16:44:50 +01:00
bool String : : ends_with ( const String & p_string ) const {
2021-01-19 13:14:25 +01:00
int l = p_string . length ( ) ;
2021-02-05 20:12:25 +01:00
if ( l > length ( ) ) {
return false ;
}
2021-01-19 13:14:25 +01:00
if ( l = = 0 ) {
return true ;
}
2021-02-05 20:12:25 +01:00
const char32_t * p = & p_string [ 0 ] ;
const char32_t * s = & operator [ ] ( length ( ) - l ) ;
for ( int i = 0 ; i < l ; i + + ) {
if ( p [ i ] ! = s [ i ] ) {
return false ;
}
2020-05-14 16:41:43 +02:00
}
2021-02-05 20:12:25 +01:00
return true ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool String : : begins_with ( const String & p_string ) const {
2021-02-05 20:12:25 +01:00
int l = p_string . length ( ) ;
if ( l > length ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( l = = 0 ) {
2014-02-10 02:10:30 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2021-02-05 20:12:25 +01:00
const char32_t * p = & p_string [ 0 ] ;
const char32_t * s = & operator [ ] ( 0 ) ;
2016-03-09 00:00:52 +01:00
2021-02-05 20:12:25 +01:00
for ( int i = 0 ; i < l ; i + + ) {
if ( p [ i ] ! = s [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2021-02-05 20:12:25 +01:00
return true ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
bool String : : begins_with ( const char * p_string ) const {
int l = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( l = = 0 | | ! p_string ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * str = & operator [ ] ( 0 ) ;
2017-03-05 16:44:50 +01:00
int i = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
while ( * p_string & & i < l ) {
2020-07-27 12:43:20 +02:00
if ( ( char32_t ) * p_string ! = str [ i ] ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
i + + ;
p_string + + ;
}
2016-03-09 00:00:52 +01:00
2014-02-17 21:46:25 +01:00
return * p_string = = 0 ;
2014-02-10 02:10:30 +01:00
}
2017-10-28 23:46:20 +02:00
bool String : : is_enclosed_in ( const String & p_string ) const {
return begins_with ( p_string ) & & ends_with ( p_string ) ;
}
2017-03-05 16:44:50 +01:00
bool String : : is_subsequence_of ( const String & p_string ) const {
2016-06-12 05:25:01 +02:00
return _base_is_subsequence_of ( p_string , false ) ;
}
2017-03-05 16:44:50 +01:00
bool String : : is_subsequence_ofi ( const String & p_string ) const {
2016-06-12 05:25:01 +02:00
return _base_is_subsequence_of ( p_string , true ) ;
}
2017-10-28 23:46:20 +02:00
bool String : : is_quoted ( ) const {
return is_enclosed_in ( " \" " ) | | is_enclosed_in ( " ' " ) ;
}
2019-01-18 09:29:28 +01:00
int String : : _count ( const String & p_string , int p_from , int p_to , bool p_case_insensitive ) const {
2020-12-15 13:04:21 +01:00
if ( p_string . is_empty ( ) ) {
2019-01-18 09:29:28 +01:00
return 0 ;
}
int len = length ( ) ;
int slen = p_string . length ( ) ;
if ( len < slen ) {
return 0 ;
}
String str ;
if ( p_from > = 0 & & p_to > = 0 ) {
if ( p_to = = 0 ) {
p_to = len ;
} else if ( p_from > = p_to ) {
return 0 ;
}
if ( p_from = = 0 & & p_to = = len ) {
str = String ( ) ;
2020-07-27 12:43:20 +02:00
str . copy_from_unchecked ( & get_data ( ) [ 0 ] , len ) ;
2019-01-18 09:29:28 +01:00
} else {
str = substr ( p_from , p_to - p_from ) ;
}
} else {
return 0 ;
}
int c = 0 ;
int idx = - 1 ;
do {
idx = p_case_insensitive ? str . findn ( p_string ) : str . find ( p_string ) ;
if ( idx ! = - 1 ) {
str = str . substr ( idx + slen , str . length ( ) - slen ) ;
+ + c ;
}
} while ( idx ! = - 1 ) ;
return c ;
}
int String : : count ( const String & p_string , int p_from , int p_to ) const {
return _count ( p_string , p_from , p_to , false ) ;
}
int String : : countn ( const String & p_string , int p_from , int p_to ) const {
return _count ( p_string , p_from , p_to , true ) ;
}
2017-03-05 16:44:50 +01:00
bool String : : _base_is_subsequence_of ( const String & p_string , bool case_insensitive ) const {
int len = length ( ) ;
2016-06-12 05:25:01 +02:00
if ( len = = 0 ) {
// Technically an empty string is subsequence of any string
return true ;
}
if ( len > p_string . length ( ) ) {
return false ;
}
2020-07-27 12:43:20 +02:00
const char32_t * src = & operator [ ] ( 0 ) ;
const char32_t * tgt = & p_string [ 0 ] ;
2016-06-12 05:25:01 +02:00
2017-03-05 16:44:50 +01:00
for ( ; * src & & * tgt ; tgt + + ) {
2016-06-12 05:25:01 +02:00
bool match = false ;
if ( case_insensitive ) {
2020-07-27 12:43:20 +02:00
char32_t srcc = _find_lower ( * src ) ;
char32_t tgtc = _find_lower ( * tgt ) ;
2016-06-12 05:25:01 +02:00
match = srcc = = tgtc ;
} else {
match = * src = = * tgt ;
}
if ( match ) {
src + + ;
2017-03-05 16:44:50 +01:00
if ( ! * src ) {
2016-06-12 05:25:01 +02:00
return true ;
}
}
}
return false ;
}
2014-02-10 02:10:30 +01:00
2016-06-13 19:06:03 +02:00
Vector < String > String : : bigrams ( ) const {
int n_pairs = length ( ) - 1 ;
Vector < String > b ;
2017-03-05 16:44:50 +01:00
if ( n_pairs < = 0 ) {
2016-06-13 19:06:03 +02:00
return b ;
}
b . resize ( n_pairs ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < n_pairs ; i + + ) {
2018-07-25 03:11:03 +02:00
b . write [ i ] = substr ( i , 2 ) ;
2016-06-13 19:06:03 +02:00
}
return b ;
}
// Similarity according to Sorensen-Dice coefficient
2017-03-05 16:44:50 +01:00
float String : : similarity ( const String & p_string ) const {
if ( operator = = ( p_string ) ) {
2016-06-13 19:06:03 +02:00
// Equal strings are totally similar
return 1.0f ;
}
if ( length ( ) < 2 | | p_string . length ( ) < 2 ) {
// No way to calculate similarity without a single bigram
return 0.0f ;
}
Vector < String > src_bigrams = bigrams ( ) ;
Vector < String > tgt_bigrams = p_string . bigrams ( ) ;
int src_size = src_bigrams . size ( ) ;
int tgt_size = tgt_bigrams . size ( ) ;
2021-01-25 20:46:35 +01:00
int sum = src_size + tgt_size ;
int inter = 0 ;
2016-06-13 19:06:03 +02:00
for ( int i = 0 ; i < src_size ; i + + ) {
for ( int j = 0 ; j < tgt_size ; j + + ) {
if ( src_bigrams [ i ] = = tgt_bigrams [ j ] ) {
inter + + ;
break ;
}
}
}
2017-03-05 16:44:50 +01:00
return ( 2.0f * inter ) / sum ;
2016-06-13 19:06:03 +02:00
}
2020-07-27 12:43:20 +02:00
static bool _wildcard_match ( const char32_t * p_pattern , const char32_t * p_string , bool p_case_sensitive ) {
2014-02-10 02:10:30 +01:00
switch ( * p_pattern ) {
2017-03-05 16:44:50 +01:00
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 ) ;
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool String : : match ( const String & p_wildcard ) const {
2020-05-14 16:41:43 +02:00
if ( ! p_wildcard . length ( ) | | ! length ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2020-07-27 12:43:20 +02:00
return _wildcard_match ( p_wildcard . get_data ( ) , get_data ( ) , true ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool String : : matchn ( const String & p_wildcard ) const {
2020-05-14 16:41:43 +02:00
if ( ! p_wildcard . length ( ) | | ! length ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-07-27 12:43:20 +02:00
return _wildcard_match ( p_wildcard . get_data ( ) , get_data ( ) , false ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String String : : format ( const Variant & values , String placeholder ) const {
String new_string = String ( this - > ptr ( ) ) ;
2016-11-01 15:14:48 +01:00
2017-03-05 16:44:50 +01:00
if ( values . get_type ( ) = = Variant : : ARRAY ) {
2016-11-01 15:14:48 +01:00
Array values_arr = values ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < values_arr . size ( ) ; i + + ) {
String i_as_str = String : : num_int64 ( i ) ;
2016-11-01 15:14:48 +01:00
2017-03-05 16:44:50 +01:00
if ( values_arr [ i ] . get_type ( ) = = Variant : : ARRAY ) { //Array in Array structure [["name","RobotGuy"],[0,"godot"],["strength",9000.91]]
2016-11-01 15:14:48 +01:00
Array value_arr = values_arr [ i ] ;
2017-03-05 16:44:50 +01:00
if ( value_arr . size ( ) = = 2 ) {
2016-11-01 15:14:48 +01:00
Variant v_key = value_arr [ 0 ] ;
2018-10-19 21:53:37 +02:00
String key = v_key ;
2017-03-05 16:44:50 +01:00
if ( key . left ( 1 ) = = " \" " & & key . right ( key . length ( ) - 1 ) = = " \" " ) {
key = key . substr ( 1 , key . length ( ) - 2 ) ;
2016-11-01 15:14:48 +01:00
}
Variant v_val = value_arr [ 1 ] ;
2018-10-19 21:53:37 +02:00
String val = v_val ;
2016-11-01 15:14:48 +01:00
2017-03-05 16:44:50 +01:00
if ( val . left ( 1 ) = = " \" " & & val . right ( val . length ( ) - 1 ) = = " \" " ) {
val = val . substr ( 1 , val . length ( ) - 2 ) ;
2016-11-01 15:14:48 +01:00
}
2018-07-25 12:14:48 +02:00
new_string = new_string . replace ( placeholder . replace ( " _ " , key ) , val ) ;
2017-03-05 16:44:50 +01:00
} else {
2016-11-01 15:14:48 +01:00
ERR_PRINT ( String ( " STRING.format Inner Array size != 2 " ) . ascii ( ) . get_data ( ) ) ;
}
2017-03-05 16:44:50 +01:00
} else { //Array structure ["RobotGuy","Logis","rookie"]
2016-11-01 15:14:48 +01:00
Variant v_val = values_arr [ i ] ;
2018-10-19 21:53:37 +02:00
String val = v_val ;
2016-11-01 15:14:48 +01:00
2017-03-05 16:44:50 +01:00
if ( val . left ( 1 ) = = " \" " & & val . right ( val . length ( ) - 1 ) = = " \" " ) {
val = val . substr ( 1 , val . length ( ) - 2 ) ;
2016-11-01 15:14:48 +01:00
}
2018-07-25 13:22:20 +02:00
if ( placeholder . find ( " _ " ) > - 1 ) {
new_string = new_string . replace ( placeholder . replace ( " _ " , i_as_str ) , val ) ;
} else {
new_string = new_string . replace_first ( placeholder , val ) ;
}
2016-11-01 15:14:48 +01:00
}
}
2017-03-05 16:44:50 +01:00
} else if ( values . get_type ( ) = = Variant : : DICTIONARY ) {
2016-11-01 15:14:48 +01:00
Dictionary d = values ;
List < Variant > keys ;
d . get_key_list ( & keys ) ;
2017-03-05 16:44:50 +01:00
for ( List < Variant > : : Element * E = keys . front ( ) ; E ; E = E - > next ( ) ) {
2018-10-19 21:53:37 +02:00
String key = E - > get ( ) ;
String val = d [ E - > get ( ) ] ;
2016-11-01 15:14:48 +01:00
2017-03-05 16:44:50 +01:00
if ( key . left ( 1 ) = = " \" " & & key . right ( key . length ( ) - 1 ) = = " \" " ) {
key = key . substr ( 1 , key . length ( ) - 2 ) ;
2016-11-01 15:14:48 +01:00
}
2017-03-05 16:44:50 +01:00
if ( val . left ( 1 ) = = " \" " & & val . right ( val . length ( ) - 1 ) = = " \" " ) {
val = val . substr ( 1 , val . length ( ) - 2 ) ;
2016-11-01 15:14:48 +01:00
}
2018-07-25 12:14:48 +02:00
new_string = new_string . replace ( placeholder . replace ( " _ " , key ) , val ) ;
2016-11-01 15:14:48 +01:00
}
2017-03-05 16:44:50 +01:00
} else {
2016-11-01 15:14:48 +01:00
ERR_PRINT ( String ( " Invalid type: use Array or Dictionary. " ) . ascii ( ) . get_data ( ) ) ;
}
return new_string ;
}
2017-12-11 23:38:07 +01:00
String String : : replace ( const String & p_key , const String & p_with ) const {
2014-02-10 02:10:30 +01:00
String new_string ;
2017-03-05 16:44:50 +01:00
int search_from = 0 ;
int result = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
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 ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
if ( search_from = = 0 ) {
return * this ;
}
2017-03-05 16:44:50 +01:00
new_string + = substr ( search_from , length ( ) - search_from ) ;
2014-02-10 02:10:30 +01:00
return new_string ;
}
2017-12-11 23:38:07 +01:00
String String : : replace ( const char * p_key , const char * p_with ) const {
String new_string ;
int search_from = 0 ;
int result = 0 ;
while ( ( result = find ( p_key , search_from ) ) > = 0 ) {
new_string + = substr ( search_from , result - search_from ) ;
new_string + = p_with ;
int k = 0 ;
2020-05-14 16:41:43 +02:00
while ( p_key [ k ] ! = ' \0 ' ) {
2017-12-11 23:38:07 +01:00
k + + ;
2020-05-14 16:41:43 +02:00
}
2017-12-11 23:38:07 +01:00
search_from = result + k ;
}
if ( search_from = = 0 ) {
return * this ;
}
new_string + = substr ( search_from , length ( ) - search_from ) ;
return new_string ;
}
String String : : replace_first ( const String & p_key , const String & p_with ) const {
2019-06-03 21:52:50 +02:00
int pos = find ( p_key ) ;
if ( pos > = 0 ) {
return substr ( 0 , pos ) + p_with + substr ( pos + p_key . length ( ) , length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2019-06-03 21:52:50 +02:00
return * this ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2017-12-11 23:38:07 +01:00
String String : : replacen ( const String & p_key , const String & p_with ) const {
2014-02-10 02:10:30 +01:00
String new_string ;
2017-03-05 16:44:50 +01:00
int search_from = 0 ;
int result = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
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 ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-11 23:38:07 +01:00
if ( search_from = = 0 ) {
return * this ;
}
2017-03-05 16:44:50 +01:00
new_string + = substr ( search_from , length ( ) - search_from ) ;
2014-02-10 02:10:30 +01:00
return new_string ;
}
2019-09-02 05:49:55 +02:00
String String : : repeat ( int p_count ) const {
ERR_FAIL_COND_V_MSG ( p_count < 0 , " " , " Parameter count should be a positive number. " ) ;
String new_string ;
2020-07-27 12:43:20 +02:00
const char32_t * src = this - > get_data ( ) ;
2019-09-02 05:49:55 +02:00
new_string . resize ( length ( ) * p_count + 1 ) ;
2020-07-27 12:43:20 +02:00
new_string [ length ( ) * p_count ] = 0 ;
2019-09-02 05:49:55 +02:00
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < p_count ; i + + ) {
for ( int j = 0 ; j < length ( ) ; j + + ) {
2019-09-02 05:49:55 +02:00
new_string [ i * length ( ) + j ] = src [ j ] ;
2020-05-14 16:41:43 +02:00
}
}
2019-09-02 05:49:55 +02:00
return new_string ;
}
2014-02-10 02:10:30 +01:00
String String : : left ( int p_pos ) const {
2020-05-14 16:41:43 +02:00
if ( p_pos < = 0 ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( p_pos > = length ( ) ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return substr ( 0 , p_pos ) ;
2014-02-10 02:10:30 +01:00
}
String String : : right ( int p_pos ) const {
2020-05-14 16:41:43 +02:00
if ( p_pos > = length ( ) ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( p_pos < = 0 ) {
2019-02-20 16:36:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2019-02-20 16:36:30 +01:00
2017-03-05 16:44:50 +01:00
return substr ( p_pos , ( length ( ) - p_pos ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-11-23 12:47:11 +01:00
char32_t String : : unicode_at ( int p_idx ) const {
2014-02-10 02:10:30 +01:00
ERR_FAIL_INDEX_V ( p_idx , length ( ) , 0 ) ;
return operator [ ] ( p_idx ) ;
}
2017-10-11 10:27:54 +02:00
String String : : dedent ( ) const {
String new_string ;
String indent ;
bool has_indent = false ;
bool has_text = false ;
int line_start = 0 ;
int indent_stop = - 1 ;
for ( int i = 0 ; i < length ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2017-10-11 10:27:54 +02:00
if ( c = = ' \n ' ) {
2020-05-14 16:41:43 +02:00
if ( has_text ) {
2017-10-11 10:27:54 +02:00
new_string + = substr ( indent_stop , i - indent_stop ) ;
2020-05-14 16:41:43 +02:00
}
2017-10-11 10:27:54 +02:00
new_string + = " \n " ;
has_text = false ;
line_start = i + 1 ;
indent_stop = - 1 ;
} else if ( ! has_text ) {
if ( c > 32 ) {
has_text = true ;
if ( ! has_indent ) {
has_indent = true ;
indent = substr ( line_start , i - line_start ) ;
indent_stop = i ;
}
}
if ( has_indent & & indent_stop < 0 ) {
int j = i - line_start ;
2020-05-14 16:41:43 +02:00
if ( j > = indent . length ( ) | | c ! = indent [ j ] ) {
2017-10-11 10:27:54 +02:00
indent_stop = i ;
2020-05-14 16:41:43 +02:00
}
2017-10-11 10:27:54 +02:00
}
}
}
2020-05-14 16:41:43 +02:00
if ( has_text ) {
2017-10-11 10:27:54 +02:00
new_string + = substr ( indent_stop , length ( ) - indent_stop ) ;
2020-05-14 16:41:43 +02:00
}
2017-10-11 10:27:54 +02:00
return new_string ;
}
2016-05-11 09:22:59 +02:00
String String : : strip_edges ( bool left , bool right ) const {
2017-03-05 16:44:50 +01:00
int len = length ( ) ;
int beg = 0 , end = len ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( left ) {
for ( int i = 0 ; i < len ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < = 32 ) {
2016-05-11 09:22:59 +02:00
beg + + ;
2020-05-14 16:41:43 +02:00
} else {
2016-05-11 09:22:59 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-05-11 09:22:59 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( right ) {
for ( int i = ( int ) ( len - 1 ) ; i > = 0 ; i - - ) {
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < = 32 ) {
2016-05-11 09:22:59 +02:00
end - - ;
2020-05-14 16:41:43 +02:00
} else {
2016-05-11 09:22:59 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2016-05-11 09:22:59 +02:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( beg = = 0 & & end = = len ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return substr ( beg , end - beg ) ;
2014-02-10 02:10:30 +01:00
}
String String : : strip_escapes ( ) const {
2019-05-31 15:27:53 +02:00
String new_string ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < length ( ) ; i + + ) {
2019-05-31 15:27:53 +02:00
// Escape characters on first page of the ASCII table, before 32 (Space).
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < 32 ) {
2019-05-31 15:27:53 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2019-05-31 15:27:53 +02:00
new_string + = operator [ ] ( i ) ;
2014-02-10 02:10:30 +01:00
}
2019-05-31 15:27:53 +02:00
return new_string ;
2014-02-10 02:10:30 +01:00
}
2018-07-25 03:11:03 +02:00
String String : : lstrip ( const String & p_chars ) const {
2018-04-13 16:40:27 +02:00
int len = length ( ) ;
int beg ;
for ( beg = 0 ; beg < len ; beg + + ) {
2020-05-14 16:41:43 +02:00
if ( p_chars . find_char ( get ( beg ) ) = = - 1 ) {
2018-04-13 16:40:27 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
}
2020-05-14 16:41:43 +02:00
if ( beg = = 0 ) {
2018-04-13 16:40:27 +02:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
return substr ( beg , len - beg ) ;
}
2018-07-25 03:11:03 +02:00
String String : : rstrip ( const String & p_chars ) const {
2018-04-13 16:40:27 +02:00
int len = length ( ) ;
int end ;
for ( end = len - 1 ; end > = 0 ; end - - ) {
2020-05-14 16:41:43 +02:00
if ( p_chars . find_char ( get ( end ) ) = = - 1 ) {
2018-04-13 16:40:27 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
}
2020-05-14 16:41:43 +02:00
if ( end = = len - 1 ) {
2018-04-13 16:40:27 +02:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2018-04-13 16:40:27 +02:00
return substr ( 0 , end + 1 ) ;
}
2014-02-10 02:10:30 +01:00
String String : : simplify_path ( ) const {
String s = * this ;
String drive ;
if ( s . begins_with ( " local:// " ) ) {
2017-03-05 16:44:50 +01:00
drive = " local:// " ;
s = s . substr ( 8 , s . length ( ) ) ;
2014-02-10 02:10:30 +01:00
} else if ( s . begins_with ( " res:// " ) ) {
2017-03-05 16:44:50 +01:00
drive = " res:// " ;
s = s . substr ( 6 , s . length ( ) ) ;
2014-02-10 02:10:30 +01:00
} else if ( s . begins_with ( " user:// " ) ) {
2017-03-05 16:44:50 +01:00
drive = " user:// " ;
2018-10-07 16:49:48 +02:00
s = s . substr ( 7 , s . length ( ) ) ;
2014-02-10 02:10:30 +01:00
} else if ( s . begins_with ( " / " ) | | s . begins_with ( " \\ " ) ) {
2017-03-05 16:44:50 +01:00
drive = s . substr ( 0 , 1 ) ;
s = s . substr ( 1 , s . length ( ) - 1 ) ;
2014-02-10 02:10:30 +01:00
} else {
int p = s . find ( " :/ " ) ;
2020-05-14 16:41:43 +02:00
if ( p = = - 1 ) {
2017-03-05 16:44:50 +01:00
p = s . find ( " : \\ " ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
if ( p ! = - 1 & & p < s . find ( " / " ) ) {
drive = s . substr ( 0 , p + 2 ) ;
s = s . substr ( p + 2 , s . length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
s = s . replace ( " \\ " , " / " ) ;
while ( true ) { // in case of using 2 or more slash
String compare = s . replace ( " // " , " / " ) ;
2020-05-14 16:41:43 +02:00
if ( s = = compare ) {
2017-03-05 16:44:50 +01:00
break ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
s = compare ;
2020-05-14 16:41:43 +02:00
}
2016-09-29 20:11:45 +02:00
}
2017-03-05 16:44:50 +01:00
Vector < String > dirs = s . split ( " / " , false ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < dirs . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
String d = dirs [ i ] ;
2017-03-05 16:44:50 +01:00
if ( d = = " . " ) {
2014-02-10 02:10:30 +01:00
dirs . remove ( i ) ;
i - - ;
2017-03-05 16:44:50 +01:00
} else if ( d = = " .. " ) {
if ( i = = 0 ) {
2014-02-10 02:10:30 +01:00
dirs . remove ( i ) ;
i - - ;
} else {
dirs . remove ( i ) ;
2017-03-05 16:44:50 +01:00
dirs . remove ( i - 1 ) ;
i - = 2 ;
2014-02-10 02:10:30 +01:00
}
}
}
2017-03-05 16:44:50 +01:00
s = " " ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < dirs . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2017-03-05 16:44:50 +01:00
s + = " / " ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
s + = dirs [ i ] ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
return drive + s ;
2014-02-10 02:10:30 +01:00
}
static int _humanize_digits ( int p_num ) {
2020-05-14 16:41:43 +02:00
if ( p_num < 100 ) {
2014-02-10 02:10:30 +01:00
return 2 ;
2020-05-14 16:41:43 +02:00
} else if ( p_num < 1024 ) {
2014-02-10 02:10:30 +01:00
return 1 ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2019-10-04 13:35:01 +02:00
String String : : humanize_size ( uint64_t p_size ) {
2017-03-05 16:44:50 +01:00
uint64_t _div = 1 ;
2019-09-23 13:53:43 +02:00
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 " ) ) ;
2017-03-05 16:44:50 +01:00
int prefix_idx = 0 ;
2014-02-10 02:10:30 +01:00
2020-02-03 17:59:24 +01:00
while ( prefix_idx < prefixes . size ( ) - 1 & & p_size > ( _div * 1024 ) ) {
2017-03-05 16:44:50 +01:00
_div * = 1024 ;
2014-02-10 02:10:30 +01:00
prefix_idx + + ;
}
2019-09-23 13:53:43 +02:00
const int digits = prefix_idx > 0 ? _humanize_digits ( p_size / _div ) : 0 ;
const double divisor = prefix_idx > 0 ? _div : 1 ;
2016-03-09 00:00:52 +01:00
2019-09-23 13:53:43 +02:00
return String : : num ( p_size / divisor ) . pad_decimals ( digits ) + " " + prefixes [ prefix_idx ] ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
bool String : : is_abs_path ( ) const {
2020-05-14 16:41:43 +02:00
if ( length ( ) > 1 ) {
2017-03-05 16:44:50 +01:00
return ( operator [ ] ( 0 ) = = ' / ' | | operator [ ] ( 0 ) = = ' \\ ' | | find ( " :/ " ) ! = - 1 | | find ( " : \\ " ) ! = - 1 ) ;
2020-05-14 16:41:43 +02:00
} else if ( ( length ( ) ) = = 1 ) {
2017-03-05 16:44:50 +01:00
return ( operator [ ] ( 0 ) = = ' / ' | | operator [ ] ( 0 ) = = ' \\ ' ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
bool String : : is_valid_identifier ( ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-27 12:43:20 +02:00
const char32_t * str = & operator [ ] ( 0 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len ; i + + ) {
if ( i = = 0 ) {
2020-05-14 16:41:43 +02:00
if ( str [ 0 ] > = ' 0 ' & & str [ 0 ] < = ' 9 ' ) {
2014-02-10 02:10:30 +01:00
return false ; // no start with number plz
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool valid_char = ( str [ i ] > = ' 0 ' & & str [ i ] < = ' 9 ' ) | | ( str [ i ] > = ' a ' & & str [ i ] < = ' z ' ) | | ( str [ i ] > = ' A ' & & str [ i ] < = ' Z ' ) | | str [ i ] = = ' _ ' ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! valid_char ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2020-07-27 12:43:20 +02:00
bool String : : is_valid_string ( ) const {
int l = length ( ) ;
const char32_t * src = get_data ( ) ;
bool valid = true ;
for ( int i = 0 ; i < l ; i + + ) {
valid = valid & & ( src [ i ] < 0xd800 | | ( src [ i ] > 0xdfff & & src [ i ] < = 0x10ffff ) ) ;
2015-08-26 04:00:11 +02:00
}
2020-07-27 12:43:20 +02:00
return valid ;
2015-08-26 04:00:11 +02:00
}
2020-11-30 04:43:38 +01:00
String String : : uri_encode ( ) const {
2017-03-05 16:44:50 +01:00
const CharString temp = utf8 ( ) ;
String res ;
2018-04-05 21:49:44 +02:00
for ( int i = 0 ; i < temp . length ( ) ; + + i ) {
char ord = temp [ i ] ;
2017-03-05 16:44:50 +01:00
if ( ord = = ' . ' | | ord = = ' - ' | | ord = = ' _ ' | | ord = = ' ~ ' | |
( ord > = ' a ' & & ord < = ' z ' ) | |
( ord > = ' A ' & & ord < = ' Z ' ) | |
( ord > = ' 0 ' & & ord < = ' 9 ' ) ) {
res + = ord ;
} else {
char h_Val [ 3 ] ;
2015-11-20 00:19:44 +01:00
# if defined(__GNUC__) || defined(_MSC_VER)
2021-03-31 01:45:17 +02:00
snprintf ( h_Val , 3 , " %02hhX " , ord ) ;
2015-11-20 00:19:44 +01:00
# else
2021-03-31 01:45:17 +02:00
sprintf ( h_Val , " %02hhX " , ord ) ;
2015-11-20 00:19:44 +01:00
# endif
2017-03-05 16:44:50 +01:00
res + = " % " ;
res + = h_Val ;
}
}
return res ;
2015-11-18 12:33:29 +01:00
}
2020-11-30 04:43:38 +01:00
String String : : uri_decode ( ) const {
2021-04-30 20:22:39 +02:00
CharString src = utf8 ( ) ;
CharString res ;
for ( int i = 0 ; i < src . length ( ) ; + + i ) {
if ( src [ i ] = = ' % ' & & i + 2 < src . length ( ) ) {
char ord1 = src [ i + 1 ] ;
2017-03-05 16:44:50 +01:00
if ( ( ord1 > = ' 0 ' & & ord1 < = ' 9 ' ) | | ( ord1 > = ' A ' & & ord1 < = ' Z ' ) ) {
2021-04-30 20:22:39 +02:00
char ord2 = src [ i + 2 ] ;
2017-03-05 16:44:50 +01:00
if ( ( ord2 > = ' 0 ' & & ord2 < = ' 9 ' ) | | ( ord2 > = ' A ' & & ord2 < = ' Z ' ) ) {
2019-07-08 17:15:10 +02:00
char bytes [ 3 ] = { ( char ) ord1 , ( char ) ord2 , 0 } ;
2020-04-02 01:20:12 +02:00
res + = ( char ) strtol ( bytes , nullptr , 16 ) ;
2017-03-05 16:44:50 +01:00
i + = 2 ;
}
} else {
2021-04-30 20:22:39 +02:00
res + = src [ i ] ;
2017-03-05 16:44:50 +01:00
}
2021-04-30 20:22:39 +02:00
} else if ( src [ i ] = = ' + ' ) {
2020-11-30 04:43:38 +01:00
res + = ' ' ;
2017-03-05 16:44:50 +01:00
} else {
2021-04-30 20:22:39 +02:00
res + = src [ i ] ;
2017-03-05 16:44:50 +01:00
}
}
2021-04-30 20:22:39 +02:00
return String : : utf8 ( res ) ;
2015-11-18 12:33:29 +01:00
}
2014-02-10 02:10:30 +01:00
String String : : c_unescape ( ) const {
2017-03-05 16:44:50 +01:00
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 ( " \\ \\ " , " \\ " ) ;
2014-02-10 02:10:30 +01:00
return escaped ;
}
String String : : c_escape ( ) const {
2017-03-05 16:44:50 +01:00
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 ( " \" " , " \\ \" " ) ;
2017-01-16 18:03:38 +01:00
return escaped ;
}
String String : : c_escape_multiline ( ) const {
2017-03-05 16:44:50 +01:00
String escaped = * this ;
escaped = escaped . replace ( " \\ " , " \\ \\ " ) ;
escaped = escaped . replace ( " \" " , " \\ \" " ) ;
2014-02-10 02:10:30 +01:00
return escaped ;
}
2016-01-10 19:01:06 +01:00
String String : : json_escape ( ) const {
2017-03-05 16:44:50 +01:00
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 ( " \" " , " \\ \" " ) ;
2016-01-10 19:01:06 +01:00
return escaped ;
}
2014-02-10 02:10:30 +01:00
String String : : xml_escape ( bool p_escape_quotes ) const {
2017-03-05 16:44:50 +01:00
String str = * this ;
str = str . replace ( " & " , " & " ) ;
str = str . replace ( " < " , " < " ) ;
str = str . replace ( " > " , " > " ) ;
2014-02-10 02:10:30 +01:00
if ( p_escape_quotes ) {
2017-03-05 16:44:50 +01:00
str = str . replace ( " ' " , " ' " ) ;
str = str . replace ( " \" " , " " " ) ;
2014-02-10 02:10:30 +01:00
}
/*
2020-05-14 16:41:43 +02:00
for ( int i = 1 ; i < 32 ; i + + ) {
char chr [ 2 ] = { i , 0 } ;
str = str . replace ( chr , " &# " + String : : num ( i ) + " ; " ) ;
} */
2014-02-10 02:10:30 +01:00
return str ;
}
2020-07-27 12:43:20 +02:00
static _FORCE_INLINE_ int _xml_unescape ( const char32_t * p_src , int p_src_len , char32_t * p_dst ) {
2017-03-05 16:44:50 +01:00
int len = 0 ;
while ( p_src_len ) {
if ( * p_src = = ' & ' ) {
int eat = 0 ;
2014-06-11 15:41:03 +02:00
2017-03-05 16:44:50 +01:00
if ( p_src_len > = 4 & & p_src [ 1 ] = = ' # ' ) {
2020-07-27 12:43:20 +02:00
char32_t c = 0 ;
2021-02-11 16:33:55 +01:00
bool overflow = false ;
if ( p_src [ 2 ] = = ' x ' ) {
// Hex entity &#x<num>;
for ( int i = 3 ; i < p_src_len ; i + + ) {
eat = i + 1 ;
char32_t ct = p_src [ i ] ;
if ( ct = = ' ; ' ) {
break ;
} 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 > ( UINT32_MAX > > 4 ) ) {
overflow = true ;
break ;
}
c < < = 4 ;
c | = ct ;
}
} else {
// Decimal entity &#<num>;
for ( int i = 2 ; i < p_src_len ; i + + ) {
eat = i + 1 ;
char32_t ct = p_src [ i ] ;
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 < = UINT32_MAX ) {
c = ( char32_t ) val ;
} else {
overflow = true ;
}
2014-06-11 15:41:03 +02:00
}
}
2021-02-11 16:33:55 +01:00
// Value must be non-zero, in the range of char32_t,
// actually end with ';'. If invalid, leave the entity as-is
if ( c = = ' \0 ' | | overflow | | p_src [ eat - 1 ] ! = ' ; ' ) {
eat = 1 ;
c = * p_src ;
}
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = c ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
2017-03-05 16:44:50 +01:00
} else if ( p_src_len > = 4 & & p_src [ 1 ] = = ' g ' & & p_src [ 2 ] = = ' t ' & & p_src [ 3 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = ' > ' ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
eat = 4 ;
} else if ( p_src_len > = 4 & & p_src [ 1 ] = = ' l ' & & p_src [ 2 ] = = ' t ' & & p_src [ 3 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = ' < ' ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
eat = 4 ;
} else if ( p_src_len > = 5 & & p_src [ 1 ] = = ' a ' & & p_src [ 2 ] = = ' m ' & & p_src [ 3 ] = = ' p ' & & p_src [ 4 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = ' & ' ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
eat = 5 ;
} else if ( p_src_len > = 6 & & p_src [ 1 ] = = ' q ' & & p_src [ 2 ] = = ' u ' & & p_src [ 3 ] = = ' o ' & & p_src [ 4 ] = = ' t ' & & p_src [ 5 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = ' " ' ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
eat = 6 ;
} else if ( p_src_len > = 6 & & p_src [ 1 ] = = ' a ' & & p_src [ 2 ] = = ' p ' & & p_src [ 3 ] = = ' o ' & & p_src [ 4 ] = = ' s ' & & p_src [ 5 ] = = ' ; ' ) {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = ' \' ' ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
eat = 6 ;
2014-06-11 15:41:03 +02:00
} else {
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = * p_src ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
eat = 1 ;
2014-06-11 15:41:03 +02:00
}
2020-05-14 16:41:43 +02:00
if ( p_dst ) {
2014-06-11 15:41:03 +02:00
p_dst + + ;
2020-05-14 16:41:43 +02:00
}
2014-06-11 15:41:03 +02:00
len + + ;
2017-03-05 16:44:50 +01:00
p_src + = eat ;
p_src_len - = eat ;
2014-06-11 15:41:03 +02:00
} else {
if ( p_dst ) {
2017-03-05 16:44:50 +01:00
* p_dst = * p_src ;
2014-06-11 15:41:03 +02:00
p_dst + + ;
}
len + + ;
p_src + + ;
p_src_len - - ;
}
}
return len ;
}
2014-02-10 02:10:30 +01:00
2014-06-11 15:41:03 +02:00
String String : : xml_unescape ( ) const {
String str ;
int l = length ( ) ;
2020-07-27 12:43:20 +02:00
int len = _xml_unescape ( get_data ( ) , l , nullptr ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-06-11 15:41:03 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
str . resize ( len + 1 ) ;
2020-07-27 12:43:20 +02:00
_xml_unescape ( get_data ( ) , l , str . ptrw ( ) ) ;
2017-03-05 16:44:50 +01:00
str [ len ] = 0 ;
2014-02-10 02:10:30 +01:00
return str ;
}
String String : : pad_decimals ( int p_digits ) const {
2017-03-05 16:44:50 +01:00
String s = * this ;
2014-02-10 02:10:30 +01:00
int c = s . find ( " . " ) ;
2017-03-05 16:44:50 +01:00
if ( c = = - 1 ) {
if ( p_digits < = 0 ) {
2014-02-10 02:10:30 +01:00
return s ;
}
2017-03-05 16:44:50 +01:00
s + = " . " ;
c = s . length ( ) - 1 ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
if ( p_digits < = 0 ) {
return s . substr ( 0 , c ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( s . length ( ) - ( c + 1 ) > p_digits ) {
s = s . substr ( 0 , c + p_digits + 1 ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
while ( s . length ( ) - ( c + 1 ) < p_digits ) {
s + = " 0 " ;
2014-02-10 02:10:30 +01:00
}
}
return s ;
}
String String : : pad_zeros ( int p_digits ) const {
2017-03-05 16:44:50 +01:00
String s = * this ;
2014-02-10 02:10:30 +01:00
int end = s . find ( " . " ) ;
2017-03-05 16:44:50 +01:00
if ( end = = - 1 ) {
end = s . length ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( end = = 0 ) {
2014-02-10 02:10:30 +01:00
return s ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int begin = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( begin < end & & ( s [ begin ] < ' 0 ' | | s [ begin ] > ' 9 ' ) ) {
2014-02-10 02:10:30 +01:00
begin + + ;
}
2020-05-14 16:41:43 +02:00
if ( begin > = end ) {
2014-02-10 02:10:30 +01:00
return s ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( end - begin < p_digits ) {
s = s . insert ( begin , " 0 " ) ;
2014-02-10 02:10:30 +01:00
end + + ;
}
return s ;
}
2018-04-13 16:40:27 +02:00
String String : : trim_prefix ( const String & p_prefix ) const {
String s = * this ;
if ( s . begins_with ( p_prefix ) ) {
return s . substr ( p_prefix . length ( ) , s . length ( ) - p_prefix . length ( ) ) ;
}
return s ;
}
String String : : trim_suffix ( const String & p_suffix ) const {
String s = * this ;
if ( s . ends_with ( p_suffix ) ) {
return s . substr ( 0 , s . length ( ) - p_suffix . length ( ) ) ;
}
return s ;
}
2014-02-10 02:10:30 +01:00
bool String : : is_valid_integer ( ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int from = 0 ;
2020-05-14 16:41:43 +02:00
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) ) {
2014-02-10 02:10:30 +01:00
from + + ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = from ; i < len ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( operator [ ] ( i ) < ' 0 ' | | operator [ ] ( i ) > ' 9 ' ) {
2014-02-10 02:10:30 +01:00
return false ; // no start with number plz
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2016-10-20 14:58:00 +02:00
bool String : : is_valid_hex_number ( bool p_with_prefix ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2018-12-24 19:06:35 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-12-24 19:06:35 +01:00
int from = 0 ;
2020-05-14 16:41:43 +02:00
if ( len ! = 1 & & ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) ) {
2016-10-20 14:58:00 +02:00
from + + ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
if ( p_with_prefix ) {
2020-05-14 16:41:43 +02:00
if ( len < 3 ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
if ( operator [ ] ( from ) ! = ' 0 ' | | operator [ ] ( from + 1 ) ! = ' x ' ) {
2016-10-20 14:58:00 +02:00
return false ;
2018-04-18 22:20:39 +02:00
}
2016-10-20 14:58:00 +02:00
from + = 2 ;
2018-04-18 22:20:39 +02:00
}
2016-10-20 14:58:00 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = from ; i < len ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = operator [ ] ( i ) ;
2020-05-14 16:41:43 +02:00
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | ( c > = ' a ' & & c < = ' f ' ) | | ( c > = ' A ' & & c < = ' F ' ) ) {
2016-10-20 14:58:00 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
return false ;
2018-04-18 22:20:39 +02:00
}
2016-10-20 14:58:00 +02:00
return true ;
2020-05-19 15:46:49 +02:00
}
2016-10-20 14:58:00 +02:00
2014-02-10 02:10:30 +01:00
bool String : : is_valid_float ( ) const {
int len = length ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int from = 0 ;
if ( operator [ ] ( 0 ) = = ' + ' | | operator [ ] ( 0 ) = = ' - ' ) {
2014-02-10 02:10:30 +01:00
from + + ;
}
2017-03-05 16:44:50 +01:00
bool exponent_found = false ;
bool period_found = false ;
bool sign_found = false ;
bool exponent_values_found = false ;
bool numbers_found = false ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = from ; i < len ; i + + ) {
if ( operator [ ] ( i ) > = ' 0 ' & & operator [ ] ( i ) < = ' 9 ' ) {
2020-05-14 16:41:43 +02:00
if ( exponent_found ) {
2017-03-05 16:44:50 +01:00
exponent_values_found = true ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
numbers_found = true ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
} else if ( numbers_found & & ! exponent_found & & operator [ ] ( i ) = = ' e ' ) {
exponent_found = true ;
} else if ( ! period_found & & ! exponent_found & & operator [ ] ( i ) = = ' . ' ) {
period_found = true ;
} else if ( ( operator [ ] ( i ) = = ' - ' | | operator [ ] ( i ) = = ' + ' ) & & exponent_found & & ! exponent_values_found & & ! sign_found ) {
sign_found = true ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return false ; // no start with number plz
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return numbers_found ;
}
2017-03-05 16:44:50 +01:00
String String : : path_to_file ( const String & p_path ) const {
2019-12-14 11:06:29 +01:00
// Don't get base dir for src, this is expected to be a dir already.
String src = this - > replace ( " \\ " , " / " ) ;
2017-03-05 16:44:50 +01:00
String dst = p_path . replace ( " \\ " , " / " ) . get_base_dir ( ) ;
2015-05-04 20:06:41 +02:00
String rel = src . path_to ( dst ) ;
2020-05-14 16:41:43 +02:00
if ( rel = = dst ) { // failed
2015-05-04 20:06:41 +02:00
return p_path ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
return rel + p_path . get_file ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String String : : path_to ( const String & p_path ) const {
String src = this - > replace ( " \\ " , " / " ) ;
String dst = p_path . replace ( " \\ " , " / " ) ;
2020-05-14 16:41:43 +02:00
if ( ! src . ends_with ( " / " ) ) {
2017-03-05 16:44:50 +01:00
src + = " / " ;
2020-05-14 16:41:43 +02:00
}
if ( ! dst . ends_with ( " / " ) ) {
2017-03-05 16:44:50 +01:00
dst + = " / " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
String base ;
if ( src . begins_with ( " res:// " ) & & dst . begins_with ( " res:// " ) ) {
2017-03-05 16:44:50 +01:00
base = " res:/ " ;
src = src . replace ( " res:// " , " / " ) ;
dst = dst . replace ( " res:// " , " / " ) ;
2014-02-10 02:10:30 +01:00
} else if ( src . begins_with ( " user:// " ) & & dst . begins_with ( " user:// " ) ) {
2017-03-05 16:44:50 +01:00
base = " user:/ " ;
src = src . replace ( " user:// " , " / " ) ;
dst = dst . replace ( " user:// " , " / " ) ;
2014-02-10 02:10:30 +01:00
} else if ( src . begins_with ( " / " ) & & dst . begins_with ( " / " ) ) {
//nothing
} else {
//dos style
2017-03-05 16:44:50 +01:00
String src_begin = src . get_slicec ( ' / ' , 0 ) ;
String dst_begin = dst . get_slicec ( ' / ' , 0 ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( src_begin ! = dst_begin ) {
2015-05-04 20:06:41 +02:00
return p_path ; //impossible to do this
2020-05-14 16:41:43 +02:00
}
2015-05-04 20:06:41 +02:00
2017-03-05 16:44:50 +01:00
base = src_begin ;
src = src . substr ( src_begin . length ( ) , src . length ( ) ) ;
dst = dst . substr ( dst_begin . length ( ) , dst . length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
//remove leading and trailing slash and split
2017-03-05 16:44:50 +01:00
Vector < String > src_dirs = src . substr ( 1 , src . length ( ) - 2 ) . split ( " / " ) ;
Vector < String > dst_dirs = dst . substr ( 1 , dst . length ( ) - 2 ) . split ( " / " ) ;
2014-02-10 02:10:30 +01:00
//find common parent
2017-03-05 16:44:50 +01:00
int common_parent = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2020-05-14 16:41:43 +02:00
if ( src_dirs . size ( ) = = common_parent ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
if ( dst_dirs . size ( ) = = common_parent ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
if ( src_dirs [ common_parent ] ! = dst_dirs [ common_parent ] ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
common_parent + + ;
}
common_parent - - ;
String dir ;
2017-03-05 16:44:50 +01:00
for ( int i = src_dirs . size ( ) - 1 ; i > common_parent ; i - - ) {
dir + = " ../ " ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = common_parent + 1 ; i < dst_dirs . size ( ) ; i + + ) {
dir + = dst_dirs [ i ] + " / " ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
if ( dir . length ( ) = = 0 ) {
2017-03-05 16:44:50 +01:00
dir = " ./ " ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return dir ;
}
bool String : : is_valid_html_color ( ) const {
return Color : : html_is_valid ( * this ) ;
}
2019-04-09 00:18:03 +02:00
bool String : : is_valid_filename ( ) const {
String stripped = strip_edges ( ) ;
if ( * this ! = stripped ) {
return false ;
}
if ( stripped = = String ( ) ) {
return false ;
}
2019-06-26 15:08:25 +02:00
return ! ( find ( " : " ) ! = - 1 | | find ( " / " ) ! = - 1 | | find ( " \\ " ) ! = - 1 | | find ( " ? " ) ! = - 1 | | find ( " * " ) ! = - 1 | | find ( " \" " ) ! = - 1 | | find ( " | " ) ! = - 1 | | find ( " % " ) ! = - 1 | | find ( " < " ) ! = - 1 | | find ( " > " ) ! = - 1 ) ;
2019-04-09 00:18:03 +02:00
}
2014-02-10 02:10:30 +01:00
bool String : : is_valid_ip_address ( ) const {
2016-10-20 14:58:00 +02:00
if ( find ( " : " ) > = 0 ) {
Vector < String > ip = split ( " : " ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < ip . size ( ) ; i + + ) {
2016-10-20 14:58:00 +02:00
String n = ip [ i ] ;
2020-12-15 13:04:21 +01:00
if ( n . is_empty ( ) ) {
2016-10-20 14:58:00 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
if ( n . is_valid_hex_number ( false ) ) {
2021-01-28 13:39:05 +01:00
int64_t nint = n . hex_to_int ( ) ;
2020-05-14 16:41:43 +02:00
if ( nint < 0 | | nint > 0xffff ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
continue ;
2020-05-19 15:46:49 +02:00
}
2020-05-14 16:41:43 +02:00
if ( ! n . is_valid_ip_address ( ) ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2020-05-19 15:46:49 +02:00
}
2016-10-20 14:58:00 +02:00
} else {
Vector < String > ip = split ( " . " ) ;
2020-05-14 16:41:43 +02:00
if ( ip . size ( ) ! = 4 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < ip . size ( ) ; i + + ) {
2016-10-20 14:58:00 +02:00
String n = ip [ i ] ;
2020-05-14 16:41:43 +02:00
if ( ! n . is_valid_integer ( ) ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
int val = n . to_int ( ) ;
2020-05-14 16:41:43 +02:00
if ( val < 0 | | val > 255 ) {
2016-10-20 14:58:00 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2016-10-20 14:58:00 +02:00
}
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
return true ;
}
bool String : : is_resource_file ( ) const {
2017-03-05 16:44:50 +01:00
return begins_with ( " res:// " ) & & find ( " :: " ) = = - 1 ;
2014-02-10 02:10:30 +01:00
}
bool String : : is_rel_path ( ) const {
return ! is_abs_path ( ) ;
}
String String : : get_base_dir ( ) const {
2020-12-23 09:48:18 +01:00
int basepos = find ( " :/ " ) ;
if ( basepos = = - 1 ) {
basepos = find ( " : \\ " ) ;
}
2014-02-10 02:10:30 +01:00
String rs ;
String base ;
2017-03-05 16:44:50 +01:00
if ( basepos ! = - 1 ) {
int end = basepos + 3 ;
rs = substr ( end , length ( ) ) ;
base = substr ( 0 , end ) ;
2014-02-10 02:10:30 +01:00
} else {
if ( begins_with ( " / " ) ) {
2017-03-05 16:44:50 +01:00
rs = substr ( 1 , length ( ) ) ;
base = " / " ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
rs = * this ;
2014-02-10 02:10:30 +01:00
}
}
2020-07-03 15:26:22 +02:00
int sep = MAX ( rs . rfind ( " / " ) , rs . rfind ( " \\ " ) ) ;
2020-05-14 16:41:43 +02:00
if ( sep = = - 1 ) {
2014-02-10 02:10:30 +01:00
return base ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return base + rs . substr ( 0 , sep ) ;
2014-02-10 02:10:30 +01:00
}
String String : : get_file ( ) const {
2020-07-03 15:26:22 +02:00
int sep = MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ;
2020-05-14 16:41:43 +02:00
if ( sep = = - 1 ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return substr ( sep + 1 , length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-01-14 04:51:09 +01:00
String String : : get_extension ( ) const {
2020-07-03 15:26:22 +02:00
int pos = rfind ( " . " ) ;
if ( pos < 0 | | pos < MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ) {
2018-01-11 12:59:31 +01:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
return substr ( pos + 1 , length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String String : : plus_file ( const String & p_file ) const {
2020-12-15 13:04:21 +01:00
if ( is_empty ( ) ) {
2017-01-14 17:47:07 +01:00
return p_file ;
2020-05-14 16:41:43 +02:00
}
if ( operator [ ] ( length ( ) - 1 ) = = ' / ' | | ( p_file . size ( ) > 0 & & p_file . operator [ ] ( 0 ) = = ' / ' ) ) {
2017-03-05 16:44:50 +01:00
return * this + p_file ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
return * this + " / " + p_file ;
2014-02-10 02:10:30 +01:00
}
2019-12-20 03:03:15 +01:00
String String : : property_name_encode ( ) const {
// Escape and quote strings with extended ASCII or further Unicode characters
// as well as '"', '=' or ' ' (32)
2020-07-27 12:43:20 +02:00
const char32_t * cstr = get_data ( ) ;
2019-12-20 03:03:15 +01:00
for ( int i = 0 ; cstr [ i ] ; i + + ) {
if ( cstr [ i ] = = ' = ' | | cstr [ i ] = = ' " ' | | cstr [ i ] < 33 | | cstr [ i ] > 126 ) {
return " \" " + c_escape_multiline ( ) + " \" " ;
}
}
// Keep as is
return * this ;
}
2021-01-28 21:48:12 +01:00
// Changes made to the set of invalid characters must also be reflected in the String documentation.
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 ;
}
2017-01-14 04:51:09 +01:00
String String : : get_basename ( ) const {
2020-07-03 15:26:22 +02:00
int pos = rfind ( " . " ) ;
if ( pos < 0 | | pos < MAX ( rfind ( " / " ) , rfind ( " \\ " ) ) ) {
2014-02-10 02:10:30 +01:00
return * this ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return substr ( 0 , pos ) ;
2014-02-10 02:10:30 +01:00
}
String itos ( int64_t p_val ) {
return String : : num_int64 ( p_val ) ;
}
2019-11-01 16:16:31 +01:00
String uitos ( uint64_t p_val ) {
return String : : num_uint64 ( p_val ) ;
}
2014-02-10 02:10:30 +01:00
String rtos ( double p_val ) {
return String : : num ( p_val ) ;
}
String rtoss ( double p_val ) {
return String : : num_scientific ( p_val ) ;
}
2015-02-01 19:42:36 +01:00
// Right-pad with a character.
2017-03-05 16:44:50 +01:00
String String : : rpad ( int min_length , const String & character ) const {
2015-02-01 19:42:36 +01:00
String s = * this ;
int padding = min_length - s . length ( ) ;
if ( padding > 0 ) {
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < padding ; i + + ) {
2017-03-05 16:44:50 +01:00
s = s + character ;
2020-05-14 16:41:43 +02:00
}
2015-02-01 19:42:36 +01:00
}
return s ;
}
2020-05-14 14:29:06 +02:00
2015-02-01 19:42:36 +01:00
// Left-pad with a character.
2017-03-05 16:44:50 +01:00
String String : : lpad ( int min_length , const String & character ) const {
2015-02-01 19:42:36 +01:00
String s = * this ;
int padding = min_length - s . length ( ) ;
if ( padding > 0 ) {
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < padding ; i + + ) {
2017-03-05 16:44:50 +01:00
s = character + s ;
2020-05-14 16:41:43 +02:00
}
2015-02-01 19:42:36 +01:00
}
return s ;
}
2015-01-10 21:44:20 +01:00
// sprintf is implemented in GDScript via:
// "fish %s pie" % "frog"
// "fish %s %d pie" % ["frog", 12]
2015-02-19 16:45:49 +01:00
// In case of an error, the string returned is the error description and "error" is true.
2017-03-05 16:44:50 +01:00
String String : : sprintf ( const Array & values , bool * error ) const {
2015-01-10 21:44:20 +01:00
String formatted ;
2020-07-27 12:43:20 +02:00
char32_t * self = ( char32_t * ) get_data ( ) ;
2015-01-10 21:44:20 +01:00
bool in_format = false ;
int value_index = 0 ;
2017-09-01 22:33:39 +02:00
int min_chars = 0 ;
int min_decimals = 0 ;
bool in_decimals = false ;
bool pad_with_zeroes = false ;
bool left_justified = false ;
bool show_sign = false ;
2015-01-10 21:44:20 +01:00
2020-11-05 03:01:55 +01:00
if ( error ) {
* error = true ;
}
2015-02-01 21:18:38 +01:00
2015-01-10 21:44:20 +01:00
for ( ; * self ; self + + ) {
2020-07-27 12:43:20 +02:00
const char32_t c = * self ;
2015-01-10 21:44:20 +01:00
2021-03-12 14:35:16 +01:00
if ( in_format ) { // We have % - let's see what else we get.
2015-01-10 21:44:20 +01:00
switch ( c ) {
2015-02-01 19:42:36 +01:00
case ' % ' : { // Replace %% with %
2015-01-10 21:44:20 +01:00
formatted + = chr ( c ) ;
in_format = false ;
break ;
2015-02-01 19:42:36 +01:00
}
2015-01-10 21:44:20 +01:00
case ' d ' : // Integer (signed)
case ' o ' : // Octal
case ' x ' : // Hexadecimal (lowercase)
2015-02-01 19:42:36 +01:00
case ' X ' : { // Hexadecimal (uppercase)
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
if ( ! values [ value_index ] . is_num ( ) ) {
2015-02-19 16:45:49 +01:00
return " a number is required " ;
2015-02-01 21:18:38 +01:00
}
2016-03-09 00:00:52 +01:00
2015-02-01 21:18:38 +01:00
int64_t value = values [ value_index ] ;
2017-09-01 22:33:39 +02:00
int base = 16 ;
2015-02-01 21:18:38 +01:00
bool capitalize = false ;
switch ( c ) {
2020-05-10 13:00:47 +02:00
case ' d ' :
base = 10 ;
break ;
case ' o ' :
base = 8 ;
break ;
case ' x ' :
break ;
2017-03-05 16:44:50 +01:00
case ' X ' :
base = 16 ;
capitalize = true ;
break ;
2015-02-01 21:18:38 +01:00
}
// Get basic number.
2017-02-08 02:40:35 +01:00
String str = String : : num_int64 ( ABS ( value ) , base , capitalize ) ;
int number_len = str . length ( ) ;
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
// Padding.
2020-10-01 22:06:12 +02:00
int pad_chars_count = ( value < 0 | | show_sign ) ? min_chars - 1 : min_chars ;
2015-02-01 21:18:38 +01:00
String pad_char = pad_with_zeroes ? String ( " 0 " ) : String ( " " ) ;
if ( left_justified ) {
2020-10-01 22:06:12 +02:00
str = str . rpad ( pad_chars_count , pad_char ) ;
2015-01-10 21:44:20 +01:00
} else {
2020-10-01 22:06:12 +02:00
str = str . lpad ( pad_chars_count , pad_char ) ;
2015-01-10 21:44:20 +01:00
}
2015-02-01 21:18:38 +01:00
2017-02-08 02:40:35 +01:00
// Sign.
if ( show_sign & & value > = 0 ) {
2017-03-05 16:44:50 +01:00
str = str . insert ( pad_with_zeroes ? 0 : str . length ( ) - number_len , " + " ) ;
2017-02-08 02:40:35 +01:00
} else if ( value < 0 ) {
2017-03-05 16:44:50 +01:00
str = str . insert ( pad_with_zeroes ? 0 : str . length ( ) - number_len , " - " ) ;
2017-02-08 02:40:35 +01:00
}
2015-02-01 21:18:38 +01:00
formatted + = str ;
+ + value_index ;
in_format = false ;
2015-01-10 21:44:20 +01:00
break ;
2015-02-01 19:42:36 +01:00
}
case ' f ' : { // Float
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
if ( ! values [ value_index ] . is_num ( ) ) {
2015-02-19 16:45:49 +01:00
return " a number is required " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
double value = values [ value_index ] ;
2020-06-03 13:47:47 +02:00
bool is_negative = ( value < 0 ) ;
2020-06-11 11:39:21 +02:00
String str = String : : num ( ABS ( value ) , min_decimals ) ;
2015-02-01 19:42:36 +01:00
2015-02-01 21:18:38 +01:00
// Pad decimals out.
str = str . pad_decimals ( min_decimals ) ;
2020-06-03 13:47:47 +02:00
int initial_len = str . length ( ) ;
2015-01-10 21:44:20 +01:00
2020-06-03 13:47:47 +02:00
// Padding. Leave room for sign later if required.
int pad_chars_count = ( is_negative | | show_sign ) ? min_chars - 1 : min_chars ;
String pad_char = pad_with_zeroes ? String ( " 0 " ) : String ( " " ) ;
2015-02-01 21:18:38 +01:00
if ( left_justified ) {
2020-06-03 13:47:47 +02:00
if ( pad_with_zeroes ) {
return " left justification cannot be used with zeros as the padding " ;
} else {
str = str . rpad ( pad_chars_count , pad_char ) ;
}
2015-01-10 21:44:20 +01:00
} else {
2020-06-03 13:47:47 +02:00
str = str . lpad ( pad_chars_count , pad_char ) ;
}
// Add sign if needed.
if ( show_sign | | is_negative ) {
String sign_char = is_negative ? " - " : " + " ;
if ( left_justified ) {
str = str . insert ( 0 , sign_char ) ;
} else {
str = str . insert ( pad_with_zeroes ? 0 : str . length ( ) - initial_len , sign_char ) ;
}
2015-01-10 21:44:20 +01:00
}
2015-02-01 21:18:38 +01:00
formatted + = str ;
+ + value_index ;
in_format = false ;
2015-01-10 21:44:20 +01:00
break ;
2015-02-01 19:42:36 +01:00
}
case ' s ' : { // String
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
String str = values [ value_index ] ;
// Padding.
if ( left_justified ) {
str = str . rpad ( min_chars ) ;
} else {
str = str . lpad ( min_chars ) ;
}
2015-01-10 21:44:20 +01:00
2015-02-01 19:42:36 +01:00
formatted + = str ;
2015-01-10 21:44:20 +01:00
+ + value_index ;
in_format = false ;
break ;
2015-02-01 19:42:36 +01:00
}
2015-02-01 21:18:38 +01:00
case ' c ' : {
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
// Convert to character.
String str ;
if ( values [ value_index ] . is_num ( ) ) {
int value = values [ value_index ] ;
if ( value < 0 ) {
2020-07-27 12:43:20 +02:00
return " unsigned integer is lower than minimum " ;
} else if ( value > = 0xd800 & & value < = 0xdfff ) {
return " unsigned integer is invalid Unicode character " ;
} else if ( value > 0x10ffff ) {
return " unsigned integer is greater than maximum " ;
2015-02-01 21:18:38 +01:00
}
str = chr ( values [ value_index ] ) ;
} else if ( values [ value_index ] . get_type ( ) = = Variant : : STRING ) {
str = values [ value_index ] ;
if ( str . length ( ) ! = 1 ) {
2015-02-19 16:45:49 +01:00
return " %c requires number or single-character string " ;
2015-02-01 21:18:38 +01:00
}
} else {
2015-02-19 16:45:49 +01:00
return " %c requires number or single-character string " ;
2015-02-01 21:18:38 +01:00
}
// Padding.
if ( left_justified ) {
str = str . rpad ( min_chars ) ;
} else {
str = str . lpad ( min_chars ) ;
}
formatted + = str ;
+ + value_index ;
in_format = false ;
break ;
}
2015-02-01 19:42:36 +01:00
case ' - ' : { // Left justify
left_justified = true ;
break ;
}
case ' + ' : { // Show + if positive.
show_sign = true ;
break ;
}
2017-03-05 16:44:50 +01:00
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' : {
2015-02-01 19:42:36 +01:00
int n = c - ' 0 ' ;
if ( in_decimals ) {
2015-02-01 21:18:38 +01:00
min_decimals * = 10 ;
min_decimals + = n ;
2015-02-01 19:42:36 +01:00
} else {
if ( c = = ' 0 ' & & min_chars = = 0 ) {
pad_with_zeroes = true ;
} else {
min_chars * = 10 ;
min_chars + = n ;
}
}
break ;
}
2017-09-01 22:33:39 +02:00
case ' . ' : { // Float separator.
2015-02-01 21:18:38 +01:00
if ( in_decimals ) {
2015-02-19 16:45:49 +01:00
return " too many decimal points in format " ;
2015-02-01 21:18:38 +01:00
}
2015-02-01 19:42:36 +01:00
in_decimals = true ;
2015-02-01 21:18:38 +01:00
min_decimals = 0 ; // We want to add the value manually.
2015-02-01 19:42:36 +01:00
break ;
2015-02-01 21:18:38 +01:00
}
2017-09-01 22:33:39 +02:00
case ' * ' : { // Dynamic width, based on value.
2015-02-01 21:18:38 +01:00
if ( value_index > = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not enough arguments for format string " ;
2015-02-01 21:18:38 +01:00
}
if ( ! values [ value_index ] . is_num ( ) ) {
2015-02-19 16:45:49 +01:00
return " * wants number " ;
2015-02-01 21:18:38 +01:00
}
int size = values [ value_index ] ;
if ( in_decimals ) {
min_decimals = size ;
} else {
min_chars = size ;
}
2015-01-10 21:44:20 +01:00
2015-02-01 21:18:38 +01:00
+ + value_index ;
break ;
}
2014-02-10 02:10:30 +01:00
2015-02-01 21:18:38 +01:00
default : {
2015-02-19 16:45:49 +01:00
return " unsupported format character " ;
2017-03-05 16:44:50 +01:00
}
2015-01-10 21:44:20 +01:00
}
} else { // Not in format string.
switch ( c ) {
case ' % ' :
in_format = true ;
2015-02-01 19:42:36 +01:00
// Back to defaults:
min_chars = 0 ;
2015-02-01 21:18:38 +01:00
min_decimals = 6 ;
2015-02-01 19:42:36 +01:00
pad_with_zeroes = false ;
left_justified = false ;
show_sign = false ;
in_decimals = false ;
2015-01-10 21:44:20 +01:00
break ;
default :
formatted + = chr ( c ) ;
}
}
}
2015-02-01 21:18:38 +01:00
if ( in_format ) {
2015-02-19 16:45:49 +01:00
return " incomplete format " ;
2015-02-01 21:18:38 +01:00
}
if ( value_index ! = values . size ( ) ) {
2015-02-19 16:45:49 +01:00
return " not all arguments converted during string formatting " ;
2015-02-01 21:18:38 +01:00
}
2020-11-05 03:01:55 +01:00
if ( error ) {
* error = false ;
}
2015-01-10 21:44:20 +01:00
return formatted ;
}
2016-05-04 03:25:37 +02:00
2017-10-28 23:46:20 +02:00
String String : : quote ( String quotechar ) const {
return quotechar + * this + quotechar ;
}
String String : : unquote ( ) const {
if ( ! is_quoted ( ) ) {
return * this ;
}
return substr ( 1 , length ( ) - 2 ) ;
}
2020-10-13 20:59:37 +02:00
Vector < uint8_t > String : : to_ascii_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
CharString charstr = s - > ascii ( ) ;
Vector < uint8_t > retval ;
size_t len = charstr . length ( ) ;
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , charstr . ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
Vector < uint8_t > String : : to_utf8_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
CharString charstr = s - > utf8 ( ) ;
Vector < uint8_t > retval ;
size_t len = charstr . length ( ) ;
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , charstr . ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
Vector < uint8_t > String : : to_utf16_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
Char16String charstr = s - > utf16 ( ) ;
Vector < uint8_t > retval ;
2020-08-05 08:25:28 +02:00
size_t len = charstr . length ( ) * sizeof ( char16_t ) ;
2020-10-13 20:59:37 +02:00
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , ( const void * ) charstr . ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
Vector < uint8_t > String : : to_utf32_buffer ( ) const {
const String * s = this ;
2020-12-15 13:04:21 +01:00
if ( s - > is_empty ( ) ) {
2020-10-13 20:59:37 +02:00
return Vector < uint8_t > ( ) ;
}
Vector < uint8_t > retval ;
2020-08-05 08:25:28 +02:00
size_t len = s - > length ( ) * sizeof ( char32_t ) ;
2020-10-13 20:59:37 +02:00
retval . resize ( len ) ;
uint8_t * w = retval . ptrw ( ) ;
2021-04-27 16:19:21 +02:00
memcpy ( w , ( const void * ) s - > ptr ( ) , len ) ;
2020-10-13 20:59:37 +02:00
return retval ;
}
2016-05-17 23:27:15 +02:00
# ifdef TOOLS_ENABLED
2020-07-16 10:52:06 +02:00
String TTR ( const String & p_text , const String & p_context ) {
2016-05-04 03:25:37 +02:00
if ( TranslationServer : : get_singleton ( ) ) {
2020-07-16 10:52:06 +02:00
return TranslationServer : : get_singleton ( ) - > tool_translate ( p_text , p_context ) ;
2016-05-04 03:25:37 +02:00
}
return p_text ;
}
2016-05-17 23:27:15 +02:00
2020-07-16 10:52:06 +02:00
String TTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context ) {
if ( TranslationServer : : get_singleton ( ) ) {
return TranslationServer : : get_singleton ( ) - > tool_translate_plural ( p_text , p_text_plural , p_n , p_context ) ;
}
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
return p_text ;
}
2020-08-07 13:17:12 +02:00
return p_text_plural ;
2020-07-16 10:52:06 +02:00
}
String DTR ( const String & p_text , const String & p_context ) {
2020-05-28 12:02:12 +02:00
// Comes straight from the XML, so remove indentation and any trailing whitespace.
const String text = p_text . dedent ( ) . strip_edges ( ) ;
2020-03-18 18:34:36 +01:00
if ( TranslationServer : : get_singleton ( ) ) {
2020-07-16 10:52:06 +02:00
return TranslationServer : : get_singleton ( ) - > doc_translate ( text , p_context ) ;
2020-03-18 18:34:36 +01:00
}
2020-05-28 12:02:12 +02:00
return text ;
2020-03-18 18:34:36 +01:00
}
2020-07-16 10:52:06 +02:00
String DTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context ) {
const String text = p_text . dedent ( ) . strip_edges ( ) ;
const String text_plural = p_text_plural . dedent ( ) . strip_edges ( ) ;
if ( TranslationServer : : get_singleton ( ) ) {
return TranslationServer : : get_singleton ( ) - > doc_translate_plural ( text , text_plural , p_n , p_context ) ;
}
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
return text ;
}
2020-08-07 13:17:12 +02:00
return text_plural ;
2020-07-16 10:52:06 +02:00
}
2016-05-17 23:27:15 +02:00
# endif
2020-07-16 10:52:06 +02:00
String RTR ( const String & p_text , const String & p_context ) {
2016-05-17 23:27:15 +02:00
if ( TranslationServer : : get_singleton ( ) ) {
2020-07-16 10:52:06 +02:00
String rtr = TranslationServer : : get_singleton ( ) - > tool_translate ( p_text , p_context ) ;
2017-03-05 16:44:50 +01:00
if ( rtr = = String ( ) | | rtr = = p_text ) {
2020-07-16 10:52:06 +02:00
return TranslationServer : : get_singleton ( ) - > translate ( p_text , p_context ) ;
2016-05-17 23:27:15 +02:00
} else {
return rtr ;
}
}
return p_text ;
}
2020-07-16 10:52:06 +02:00
String RTRN ( const String & p_text , const String & p_text_plural , int p_n , const String & p_context ) {
if ( TranslationServer : : get_singleton ( ) ) {
String rtr = TranslationServer : : get_singleton ( ) - > tool_translate_plural ( p_text , p_text_plural , p_n , p_context ) ;
if ( rtr = = String ( ) | | rtr = = p_text | | rtr = = p_text_plural ) {
return TranslationServer : : get_singleton ( ) - > translate_plural ( p_text , p_text_plural , p_n , p_context ) ;
} else {
return rtr ;
}
}
// Return message based on English plural rule if translation is not possible.
if ( p_n = = 1 ) {
return p_text ;
}
2020-08-07 13:17:12 +02:00
return p_text_plural ;
2020-07-16 10:52:06 +02:00
}