diff options
Diffstat (limited to 'core/string/ustring.cpp')
-rw-r--r-- | core/string/ustring.cpp | 205 |
1 files changed, 116 insertions, 89 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index bdb66526a4..d2d563c5dc 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -54,11 +54,27 @@ #define snprintf _snprintf_s #endif -#define MAX_DIGITS 6 -#define UPPERCASE(m_c) (((m_c) >= 'a' && (m_c) <= 'z') ? ((m_c) - ('a' - 'A')) : (m_c)) -#define LOWERCASE(m_c) (((m_c) >= 'A' && (m_c) <= 'Z') ? ((m_c) + ('a' - 'A')) : (m_c)) -#define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9') -#define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F')) +static const int MAX_DECIMALS = 32; + +static _FORCE_INLINE_ bool is_digit(char32_t c) { + return (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { + return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +static _FORCE_INLINE_ bool is_upper_case(char32_t c) { + return (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_lower_case(char32_t c) { + return (c >= 'a' && c <= 'z'); +} + +static _FORCE_INLINE_ char32_t lower_case(char32_t c) { + return (is_upper_case(c) ? (c + ('a' - 'A')) : c); +} const char CharString::_null = 0; const char16_t Char16String::_null = 0; @@ -275,7 +291,7 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r base = base.substr(pos + 1, base.length() - pos - 1); } else { // Anything else - if (base.get_slice_count(":") > 1) { + if (base.get_slice_count(":") > 2) { return ERR_INVALID_PARAMETER; } pos = base.rfind(":"); @@ -294,7 +310,7 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r // Port if (base.begins_with(":")) { base = base.substr(1, base.length() - 1); - if (!base.is_valid_integer()) { + if (!base.is_valid_int()) { return ERR_INVALID_PARAMETER; } r_port = base.to_int(); @@ -738,6 +754,7 @@ bool String::operator<=(const String &p_str) const { bool String::operator>(const String &p_str) const { return p_str < *this; } + bool String::operator>=(const String &p_str) const { return !(*this < p_str); } @@ -871,8 +888,8 @@ signed char String::naturalnocasecmp_to(const String &p_str) const { while (*this_str) { if (!*that_str) { return 1; - } else if (IS_DIGIT(*this_str)) { - if (!IS_DIGIT(*that_str)) { + } else if (is_digit(*this_str)) { + if (!is_digit(*that_str)) { return -1; } @@ -881,10 +898,10 @@ signed char String::naturalnocasecmp_to(const String &p_str) const { const char32_t *that_substr = that_str; // Compare lengths of both numerical sequences, ignoring leading zeros - while (IS_DIGIT(*this_str)) { + while (is_digit(*this_str)) { this_str++; } - while (IS_DIGIT(*that_str)) { + while (is_digit(*that_str)) { that_str++; } while (*this_substr == '0') { @@ -912,7 +929,7 @@ signed char String::naturalnocasecmp_to(const String &p_str) const { this_substr++; that_substr++; } - } else if (IS_DIGIT(*that_str)) { + } else if (is_digit(*that_str)) { return 1; } else { if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than @@ -939,7 +956,7 @@ const char32_t *String::get_data() const { } void String::erase(int p_pos, int p_chars) { - *this = left(p_pos) + substr(p_pos + p_chars, length() - ((p_pos + p_chars))); + *this = left(MAX(p_pos, 0)) + substr(p_pos + p_chars, length() - ((p_pos + p_chars))); } String String::capitalize() const { @@ -962,26 +979,25 @@ String String::capitalize() const { String String::camelcase_to_underscore(bool lowercase) const { const char32_t *cstr = get_data(); String new_string; - const char A = 'A', Z = 'Z'; - const char a = 'a', z = 'z'; int start_index = 0; for (int i = 1; i < this->size(); i++) { - bool is_upper = cstr[i] >= A && cstr[i] <= Z; - bool is_number = cstr[i] >= '0' && cstr[i] <= '9'; + bool is_upper = is_upper_case(cstr[i]); + bool is_number = is_digit(cstr[i]); + bool are_next_2_lower = false; bool is_next_lower = false; bool is_next_number = false; - bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z; - bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9'; + bool was_precedent_upper = is_upper_case(cstr[i - 1]); + bool was_precedent_number = is_digit(cstr[i - 1]); 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; + are_next_2_lower = is_lower_case(cstr[i + 1]) && is_lower_case(cstr[i + 2]); } 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'; + is_next_lower = is_lower_case(cstr[i + 1]); + is_next_number = is_digit(cstr[i + 1]); } const bool cond_a = is_upper && !was_precedent_upper && !was_precedent_number; @@ -1379,8 +1395,17 @@ String String::num(double p_num, int p_decimals) { } #ifndef NO_USE_STDLIB - if (p_decimals > 16) { - p_decimals = 16; + if (p_decimals < 0) { + p_decimals = 14; + const double abs_num = ABS(p_num); + if (abs_num > 10) { + // We want to align the digits to the above sane default, so we only + // need to subtract log10 for numbers with a positive power of ten. + p_decimals -= (int)floor(log10(abs_num)); + } + } + if (p_decimals > MAX_DECIMALS) { + p_decimals = MAX_DECIMALS; } char fmt[7]; @@ -1391,7 +1416,6 @@ String String::num(double p_num, int p_decimals) { fmt[1] = 'l'; fmt[2] = 'f'; fmt[3] = 0; - } else if (p_decimals < 10) { fmt[2] = '0' + p_decimals; fmt[3] = 'l'; @@ -1458,8 +1482,9 @@ String String::num(double p_num, int p_decimals) { double dec = p_num - (double)((int)p_num); int digit = 0; - if (p_decimals > MAX_DIGITS) - p_decimals = MAX_DIGITS; + if (p_decimals > MAX_DECIMALS) { + p_decimals = MAX_DECIMALS; + } int dec_int = 0; int dec_max = 0; @@ -1471,16 +1496,18 @@ String String::num(double p_num, int p_decimals) { digit++; if (p_decimals == -1) { - if (digit == MAX_DIGITS) //no point in going to infinite + if (digit == MAX_DECIMALS) { //no point in going to infinite break; + } if (dec - (double)((int)dec) < 1e-6) { break; } } - if (digit == p_decimals) + if (digit == p_decimals) { break; + } } dec *= 10; int last = (int)dec % 10; @@ -1589,7 +1616,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { return s; } -String String::num_real(double p_num) { +String String::num_real(double p_num, bool p_trailing) { if (Math::is_nan(p_num)) { return "nan"; } @@ -1604,19 +1631,34 @@ String String::num_real(double p_num) { String s; String sd; - /* integer part */ + + // Integer part. bool neg = p_num < 0; p_num = ABS(p_num); int intn = (int)p_num; - /* decimal part */ + // Decimal part. - if ((int)p_num != p_num) { - double dec = p_num - (double)((int)p_num); + if (intn != p_num) { + double dec = p_num - (double)(intn); int digit = 0; - int decimals = MAX_DIGITS; + +#if REAL_T_IS_DOUBLE + int decimals = 14; +#else + int decimals = 6; +#endif + // We want to align the digits to the above sane default, so we only + // need to subtract log10 for numbers with a positive power of ten. + if (p_num > 10) { + decimals -= (int)floor(log10(p_num)); + } + + if (decimals > MAX_DECIMALS) { + decimals = MAX_DECIMALS; + } int dec_int = 0; int dec_max = 0; @@ -1656,8 +1698,10 @@ String String::num_real(double p_num) { dec_int /= 10; } sd = '.' + decimal; - } else { + } else if (p_trailing) { sd = ".0"; + } else { + sd = ""; } if (intn == 0) { @@ -2183,9 +2227,9 @@ int64_t String::hex_to_int() const { int64_t hex = 0; while (*s) { - char32_t c = LOWERCASE(*s); + char32_t c = lower_case(*s); int64_t n; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { n = c - '0'; } else if (c >= 'a' && c <= 'f') { n = (c - 'a') + 10; @@ -2224,7 +2268,7 @@ int64_t String::bin_to_int() const { int64_t binary = 0; while (*s) { - char32_t c = LOWERCASE(*s); + char32_t c = lower_case(*s); int64_t n; if (c == '0' || c == '1') { n = c - '0'; @@ -2254,7 +2298,7 @@ int64_t String::to_int() const { for (int i = 0; i < to; i++) { char32_t c = operator[](i); - if (c >= '0' && c <= '9') { + if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; @@ -2283,7 +2327,7 @@ int64_t String::to_int(const char *p_str, int p_len) { for (int i = 0; i < to; i++) { char c = p_str[i]; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { 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; @@ -2314,7 +2358,7 @@ int64_t String::to_int(const wchar_t *p_str, int p_len) { for (int i = 0; i < to; i++) { wchar_t c = p_str[i]; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { 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; @@ -2434,7 +2478,7 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point decPt = -1; for (mantSize = 0;; mantSize += 1) { c = *p; - if (!IS_DIGIT(c)) { + if (!is_digit(c)) { if ((c != '.') || (decPt >= 0)) { break; } @@ -2509,11 +2553,11 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point } expSign = false; } - if (!IS_DIGIT(char32_t(*p))) { + if (!is_digit(char32_t(*p))) { p = pExp; goto done; } - while (IS_DIGIT(char32_t(*p))) { + while (is_digit(char32_t(*p))) { exp = exp * 10 + (*p - '0'); p += 1; } @@ -2599,7 +2643,7 @@ int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) { char32_t c = *(str++); switch (reading) { case READING_SIGN: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { reading = READING_INT; // let it fallthrough } else if (c == '-') { @@ -2616,7 +2660,7 @@ int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) { [[fallthrough]]; } case READING_INT: { - if (c >= '0' && c <= '9') { + if (is_digit(c)) { if (integer > INT64_MAX / 10) { String number(""); str = p_str; @@ -3382,17 +3426,10 @@ String String::format(const Variant &values, String placeholder) const { if (value_arr.size() == 2) { Variant v_key = value_arr[0]; String key = v_key; - if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") { - key = key.substr(1, key.length() - 2); - } Variant v_val = value_arr[1]; String val = v_val; - if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { - val = val.substr(1, val.length() - 2); - } - new_string = new_string.replace(placeholder.replace("_", key), val); } else { ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data()); @@ -3401,10 +3438,6 @@ String String::format(const Variant &values, String placeholder) const { Variant v_val = values_arr[i]; String val = v_val; - if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { - val = val.substr(1, val.length() - 2); - } - if (placeholder.find("_") > -1) { new_string = new_string.replace(placeholder.replace("_", i_as_str), val); } else { @@ -3417,19 +3450,8 @@ String String::format(const Variant &values, String placeholder) const { List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String key = E->get(); - String val = d[E->get()]; - - if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") { - key = key.substr(1, key.length() - 2); - } - - if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { - val = val.substr(1, val.length() - 2); - } - - new_string = new_string.replace(placeholder.replace("_", key), val); + for (const Variant &key : keys) { + new_string = new_string.replace(placeholder.replace("_", key), d[key]); } } else { ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); @@ -3529,6 +3551,10 @@ String String::repeat(int p_count) const { } String String::left(int p_pos) const { + if (p_pos < 0) { + p_pos = length() + p_pos; + } + if (p_pos <= 0) { return ""; } @@ -3541,15 +3567,19 @@ String String::left(int p_pos) const { } String String::right(int p_pos) const { - if (p_pos >= length()) { - return ""; + if (p_pos < 0) { + p_pos = length() + p_pos; } if (p_pos <= 0) { + return ""; + } + + if (p_pos >= length()) { return *this; } - return substr(p_pos, (length() - p_pos)); + return substr(length() - p_pos); } char32_t String::unicode_at(int p_idx) const { @@ -3778,7 +3808,7 @@ String String::humanize_size(uint64_t p_size) { return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; } -bool String::is_abs_path() const { +bool String::is_absolute_path() const { if (length() > 1) { return (operator[](0) == '/' || operator[](0) == '\\' || find(":/") != -1 || find(":\\") != -1); } else if ((length()) == 1) { @@ -3799,12 +3829,12 @@ bool String::is_valid_identifier() const { for (int i = 0; i < len; i++) { if (i == 0) { - if (str[0] >= '0' && str[0] <= '9') { + if (is_digit(str[0])) { return false; // no start with number plz } } - bool valid_char = (str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z') || str[i] == '_'; + bool valid_char = is_digit(str[i]) || is_lower_case(str[i]) || is_upper_case(str[i]) || str[i] == '_'; if (!valid_char) { return false; @@ -3829,10 +3859,7 @@ String String::uri_encode() const { String res; for (int i = 0; i < temp.length(); ++i) { char ord = temp[i]; - if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || - (ord >= 'a' && ord <= 'z') || - (ord >= 'A' && ord <= 'Z') || - (ord >= '0' && ord <= '9')) { + if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || is_lower_case(ord) || is_upper_case(ord) || is_digit(ord)) { res += ord; } else { char h_Val[3]; @@ -3854,9 +3881,9 @@ String String::uri_decode() const { for (int i = 0; i < src.length(); ++i) { if (src[i] == '%' && i + 2 < src.length()) { char ord1 = src[i + 1]; - if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { + if (is_digit(ord1) || is_upper_case(ord1)) { char ord2 = src[i + 2]; - if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { + if (is_digit(ord2) || is_upper_case(ord2)) { char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, nullptr, 16); i += 2; @@ -3962,7 +3989,7 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch char32_t ct = p_src[i]; if (ct == ';') { break; - } else if (ct >= '0' && ct <= '9') { + } else if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = (ct - 'a') + 10; @@ -4145,7 +4172,7 @@ String String::trim_suffix(const String &p_suffix) const { return s; } -bool String::is_valid_integer() const { +bool String::is_valid_int() const { int len = length(); if (len == 0) { @@ -4190,7 +4217,7 @@ bool String::is_valid_hex_number(bool p_with_prefix) const { for (int i = from; i < len; i++) { char32_t c = operator[](i); - if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + if (is_hex_digit(c)) { continue; } return false; @@ -4218,7 +4245,7 @@ bool String::is_valid_float() const { bool numbers_found = false; for (int i = from; i < len; i++) { - if (operator[](i) >= '0' && operator[](i) <= '9') { + if (is_digit(operator[](i))) { if (exponent_found) { exponent_values_found = true; } else { @@ -4370,7 +4397,7 @@ bool String::is_valid_ip_address() const { } for (int i = 0; i < ip.size(); i++) { String n = ip[i]; - if (!n.is_valid_integer()) { + if (!n.is_valid_int()) { return false; } int val = n.to_int(); @@ -4388,7 +4415,7 @@ bool String::is_resource_file() const { } bool String::is_rel_path() const { - return !is_abs_path(); + return !is_absolute_path(); } String String::get_base_dir() const { |