diff options
Diffstat (limited to 'core/ustring.cpp')
-rw-r--r-- | core/ustring.cpp | 364 |
1 files changed, 262 insertions, 102 deletions
diff --git a/core/ustring.cpp b/core/ustring.cpp index e46896ca85..07caa3a018 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #include "ustring.h" #include "core/color.h" +#include "core/crypto/crypto_core.h" #include "core/math/math_funcs.h" #include "core/os/memory.h" #include "core/print_string.h" @@ -38,10 +39,8 @@ #include "core/ucaps.h" #include "core/variant.h" -#include "thirdparty/misc/md5.h" -#include "thirdparty/misc/sha256.h" - #include <wchar.h> +#include <cstdint> #ifndef NO_USE_STDLIB #include <stdio.h> @@ -49,7 +48,7 @@ #endif #if defined(MINGW_ENABLED) || defined(_MSC_VER) -#define snprintf _snprintf +#define snprintf _snprintf_s #endif #define MAX_DIGITS 6 @@ -58,6 +57,9 @@ #define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9') #define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F')) +const char CharString::_null = 0; +const CharType String::_null = 0; + bool is_symbol(CharType c) { return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); } @@ -120,6 +122,31 @@ const char *CharString::get_data() const { return ""; } +CharString &CharString::operator=(const char *p_cstr) { + + copy_from(p_cstr); + return *this; +} + +void CharString::copy_from(const char *p_cstr) { + + if (!p_cstr) { + resize(0); + return; + } + + size_t len = strlen(p_cstr); + + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include terminating null char + + strcpy(ptrw(), p_cstr); +} + void String::copy_from(const char *p_cstr) { if (!p_cstr) { @@ -179,7 +206,7 @@ void String::copy_from_unchecked(const CharType *p_char, const int p_length) { resize(p_length + 1); set(p_length, 0); - CharType *dst = &operator[](0); + CharType *dst = ptrw(); for (int i = 0; i < p_length; i++) { dst[i] = p_char[i]; @@ -250,7 +277,7 @@ String &String::operator+=(const String &p_str) { resize(length() + p_str.size()); const CharType *src = p_str.c_str(); - CharType *dst = &operator[](0); + CharType *dst = ptrw(); set(length(), 0); @@ -289,7 +316,7 @@ String &String::operator+=(const char *p_str) { resize(from + src_len + 1); - CharType *dst = &operator[](0); + CharType *dst = ptrw(); set(length(), 0); @@ -453,8 +480,6 @@ signed char String::nocasecmp_to(const String &p_str) const { this_str++; that_str++; } - - return 0; //should never reach anyway } signed char String::casecmp_to(const String &p_str) const { @@ -485,8 +510,6 @@ signed char String::casecmp_to(const String &p_str) const { this_str++; that_str++; } - - return 0; //should never reach anyway } signed char String::naturalnocasecmp_to(const String &p_str) const { @@ -586,6 +609,8 @@ String String::camelcase_to_underscore(bool lowercase) const { bool is_upper = cstr[i] >= A && cstr[i] <= Z; bool is_number = cstr[i] >= '0' && cstr[i] <= '9'; bool are_next_2_lower = false; + bool is_next_lower = false; + bool is_next_number = false; bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z; bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9'; @@ -593,7 +618,18 @@ String String::camelcase_to_underscore(bool lowercase) const { are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z; } - bool should_split = ((is_upper && !was_precedent_upper && !was_precedent_number) || (was_precedent_upper && is_upper && are_next_2_lower) || (is_number && !was_precedent_number)); + if (i + 1 < this->size()) { + is_next_lower = cstr[i + 1] >= a && cstr[i + 1] <= z; + is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9'; + } + + const bool cond_a = is_upper && !was_precedent_upper && !was_precedent_number; + const bool cond_b = was_precedent_upper && is_upper && are_next_2_lower; + const bool cond_c = is_number && !was_precedent_number; + const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower; + const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number); + + bool should_split = cond_a || cond_b || cond_c || can_break_number_letter || can_break_letter_number; if (should_split) { new_string += this->substr(start_index, i - start_index) + "_"; start_index = i; @@ -690,8 +726,6 @@ String String::get_slicec(CharType p_splitter, int p_slice) const { i++; } - - return String(); //no find! } Vector<String> String::split_spaces() const { @@ -745,7 +779,7 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p if (p_allow_empty || (end > from)) { if (p_maxsplit <= 0) ret.push_back(substr(from, end - from)); - else if (p_maxsplit > 0) { + else { // Put rest of the string and leave cycle. if (p_maxsplit == ret.size()) { @@ -1343,6 +1377,9 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { #define _UNICERROR(m_err) print_line("Unicode error: " + String(m_err)); + if (!p_utf8) + return true; + String aux; int cstr_size = 0; @@ -1418,7 +1455,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) { } resize(str_size + 1); - CharType *dst = &operator[](0); + CharType *dst = ptrw(); dst[str_size] = 0; while (cstr_size) { @@ -1632,6 +1669,7 @@ int String::hex_to_int(bool p_with_prefix) const { return 0; } + ERR_FAIL_COND_V_MSG(hex > INT32_MAX / 16, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); hex *= 16; hex += n; s++; @@ -1673,6 +1711,7 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { return 0; } + ERR_FAIL_COND_V_MSG(hex > INT64_MAX / 16, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); hex *= 16; hex += n; s++; @@ -1681,6 +1720,46 @@ int64_t String::hex_to_int64(bool p_with_prefix) const { return hex * sign; } +int64_t String::bin_to_int64(bool p_with_prefix) const { + + if (p_with_prefix && length() < 3) + return 0; + + const CharType *s = ptr(); + + int64_t sign = s[0] == '-' ? -1 : 1; + + if (sign < 0) { + s++; + } + + if (p_with_prefix) { + if (s[0] != '0' || s[1] != 'b') + return 0; + s += 2; + } + + int64_t binary = 0; + + while (*s) { + + CharType c = LOWERCASE(*s); + int64_t n; + if (c == '0' || c == '1') { + n = c - '0'; + } else { + return 0; + } + + ERR_FAIL_COND_V_MSG(binary > INT64_MAX / 2, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + binary *= 2; + binary += n; + s++; + } + + return binary * sign; +} + int String::to_int() const { if (length() == 0) @@ -1696,6 +1775,7 @@ int String::to_int() const { CharType c = operator[](i); if (c >= '0' && c <= '9') { + ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1723,6 +1803,7 @@ int64_t String::to_int64() const { CharType c = operator[](i); if (c >= '0' && c <= '9') { + ERR_FAIL_COND_V_MSG(integer > INT64_MAX / 10, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1753,6 +1834,7 @@ int String::to_int(const char *p_str, int p_len) { char c = p_str[i]; if (c >= '0' && c <= '9') { + ERR_FAIL_COND_V_MSG(integer > INT32_MAX / 10, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); integer *= 10; integer += c - '0'; @@ -1828,8 +1910,8 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point int sign, expSign = false; double fraction, dblExp; const double *d; - register const C *p; - register int c; + const C *p; + int c; int exp = 0; /* Exponent read from "EX" field. */ int fracExp = 0; /* Exponent that derives from the fractional * part. Under normal circumstances, it is @@ -2065,6 +2147,14 @@ int64_t String::to_int(const CharType *p_str, int p_len) { if (c >= '0' && c <= '9') { + if (integer > INT64_MAX / 10) { + String number(""); + str = p_str; + while (*str && str != limit) { + number += *(str++); + } + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + } integer *= 10; integer += c - '0'; } else { @@ -2177,54 +2267,63 @@ uint64_t String::hash64() const { String String::md5_text() const { CharString cs = utf8(); - MD5_CTX ctx; - MD5Init(&ctx); - MD5Update(&ctx, (unsigned char *)cs.ptr(), cs.length()); - MD5Final(&ctx); - return String::md5(ctx.digest); + unsigned char hash[16]; + CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); + return String::hex_encode_buffer(hash, 16); +} + +String String::sha1_text() const { + CharString cs = utf8(); + unsigned char hash[20]; + CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash); + return String::hex_encode_buffer(hash, 20); } String String::sha256_text() const { CharString cs = utf8(); unsigned char hash[32]; - sha256_context ctx; - sha256_init(&ctx); - sha256_hash(&ctx, (unsigned char *)cs.ptr(), cs.length()); - sha256_done(&ctx, hash); + CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash); return String::hex_encode_buffer(hash, 32); } Vector<uint8_t> String::md5_buffer() const { CharString cs = utf8(); - MD5_CTX ctx; - MD5Init(&ctx); - MD5Update(&ctx, (unsigned char *)cs.ptr(), cs.length()); - MD5Final(&ctx); + unsigned char hash[16]; + CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash); Vector<uint8_t> ret; ret.resize(16); for (int i = 0; i < 16; i++) { - ret.write[i] = ctx.digest[i]; - }; - + ret.write[i] = hash[i]; + } return ret; }; +Vector<uint8_t> String::sha1_buffer() const { + CharString cs = utf8(); + unsigned char hash[20]; + CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash); + + Vector<uint8_t> ret; + ret.resize(20); + for (int i = 0; i < 20; i++) { + ret.write[i] = hash[i]; + } + + return ret; +} + Vector<uint8_t> String::sha256_buffer() const { CharString cs = utf8(); unsigned char hash[32]; - sha256_context ctx; - sha256_init(&ctx); - sha256_hash(&ctx, (unsigned char *)cs.ptr(), cs.length()); - sha256_done(&ctx, hash); + CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash); Vector<uint8_t> ret; ret.resize(32); for (int i = 0; i < 32; i++) { ret.write[i] = hash[i]; } - return ret; } @@ -2248,6 +2347,9 @@ String String::insert(int p_at_pos, const String &p_string) const { } String String::substr(int p_from, int p_chars) const { + if (p_chars == -1) + p_chars = length() - p_from; + if (empty() || p_from < 0 || p_from >= length() || p_chars <= 0) return ""; @@ -2377,6 +2479,10 @@ int String::find(const char *p_str, int p_from) const { return -1; } +int String::find_char(const CharType &p_char, int p_from) const { + return _cowdata.find(p_char, p_from); +} + int String::findmk(const Vector<String> &p_keys, int p_from, int *r_key) const { if (p_from < 0) @@ -2638,6 +2744,51 @@ bool String::is_quoted() const { return is_enclosed_in("\"") || is_enclosed_in("'"); } +int String::_count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const { + if (p_string.empty()) { + return 0; + } + int len = length(); + int slen = p_string.length(); + if (len < slen) { + return 0; + } + String str; + if (p_from >= 0 && p_to >= 0) { + if (p_to == 0) { + p_to = len; + } else if (p_from >= p_to) { + return 0; + } + if (p_from == 0 && p_to == len) { + str = String(); + str.copy_from_unchecked(&c_str()[0], len); + } else { + str = substr(p_from, p_to - p_from); + } + } else { + return 0; + } + int c = 0; + int idx = -1; + do { + idx = p_case_insensitive ? str.findn(p_string) : str.find(p_string); + if (idx != -1) { + str = str.substr(idx + slen, str.length() - slen); + ++c; + } + } while (idx != -1); + return c; +} + +int String::count(const String &p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, false); +} + +int String::countn(const String &p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, true); +} + bool String::_base_is_subsequence_of(const String &p_string, bool case_insensitive) const { int len = length(); @@ -2761,16 +2912,13 @@ String String::format(const Variant &values, String placeholder) const { if (value_arr.size() == 2) { Variant v_key = value_arr[0]; - String key; - - key = v_key.get_construct_string(); + 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; - val = v_val.get_construct_string(); + String val = v_val; if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { val = val.substr(1, val.length() - 2); @@ -2782,8 +2930,7 @@ String String::format(const Variant &values, String placeholder) const { } } else { //Array structure ["RobotGuy","Logis","rookie"] Variant v_val = values_arr[i]; - String val; - val = v_val.get_construct_string(); + String val = v_val; if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { val = val.substr(1, val.length() - 2); @@ -2802,8 +2949,8 @@ String String::format(const Variant &values, String placeholder) const { d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String key = E->get().get_construct_string(); - String val = d[E->get()].get_construct_string(); + 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); @@ -2873,16 +3020,24 @@ String String::replace(const char *p_key, const char *p_with) const { String String::replace_first(const String &p_key, const String &p_with) const { + int pos = find(p_key); + if (pos >= 0) { + return substr(0, pos) + p_with + substr(pos + p_key.length(), length()); + } + + return *this; +} +String String::replacen(const String &p_key, const String &p_with) const { + String new_string; int search_from = 0; int result = 0; - while ((result = find(p_key, search_from)) >= 0) { + while ((result = findn(p_key, search_from)) >= 0) { new_string += substr(search_from, result - search_from); new_string += p_with; search_from = result + p_key.length(); - break; } if (search_from == 0) { @@ -2891,28 +3046,22 @@ String String::replace_first(const String &p_key, const String &p_with) const { } new_string += substr(search_from, length() - search_from); - return new_string; } -String String::replacen(const String &p_key, const String &p_with) const { - String new_string; - int search_from = 0; - int result = 0; +String String::repeat(int p_count) const { - while ((result = findn(p_key, search_from)) >= 0) { + ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); - new_string += substr(search_from, result - search_from); - new_string += p_with; - search_from = result + p_key.length(); - } + String new_string; + const CharType *src = this->c_str(); - if (search_from == 0) { + new_string.resize(length() * p_count + 1); - return *this; - } + for (int i = 0; i < p_count; i++) + for (int j = 0; j < length(); j++) + new_string[i * length() + j] = src[j]; - new_string += substr(search_from, length() - search_from); return new_string; } @@ -2929,12 +3078,12 @@ String String::left(int p_pos) const { String String::right(int p_pos) const { - if (p_pos >= size()) - return *this; - - if (p_pos < 0) + if (p_pos >= length()) return ""; + if (p_pos <= 0) + return *this; + return substr(p_pos, (length() - p_pos)); } @@ -3019,29 +3168,16 @@ String String::strip_edges(bool left, bool right) const { String String::strip_escapes() const { - int len = length(); - int beg = 0, end = len; - + String new_string; for (int i = 0; i < length(); i++) { - if (operator[](i) <= 31) - beg++; - else - break; - } - - for (int i = (int)(length() - 1); i >= 0; i--) { - - if (operator[](i) <= 31) - end--; - else - break; + // Escape characters on first page of the ASCII table, before 32 (Space). + if (operator[](i) < 32) + continue; + new_string += operator[](i); } - if (beg == 0 && end == len) - return *this; - - return substr(beg, end - beg); + return new_string; } String String::lstrip(const String &p_chars) const { @@ -3051,7 +3187,7 @@ String String::lstrip(const String &p_chars) const { for (beg = 0; beg < len; beg++) { - if (p_chars.find(&ptr()[beg]) == -1) + if (p_chars.find_char(get(beg)) == -1) break; } @@ -3068,7 +3204,7 @@ String String::rstrip(const String &p_chars) const { for (end = len - 1; end >= 0; end--) { - if (p_chars.find(&ptr()[end]) == -1) + if (p_chars.find_char(get(end)) == -1) break; } @@ -3092,7 +3228,7 @@ String String::simplify_path() const { } else if (s.begins_with("user://")) { drive = "user://"; - s = s.substr(6, s.length()); + s = s.substr(7, s.length()); } else if (s.begins_with("/") || s.begins_with("\\")) { drive = s.substr(0, 1); @@ -3165,18 +3301,26 @@ static int _humanize_digits(int p_num) { String String::humanize_size(size_t p_size) { uint64_t _div = 1; - static const char *prefix[] = { " Bytes", " KB", " MB", " GB", "TB", " PB", "HB", "" }; + Vector<String> prefixes; + prefixes.push_back(RTR("B")); + prefixes.push_back(RTR("KiB")); + prefixes.push_back(RTR("MiB")); + prefixes.push_back(RTR("GiB")); + prefixes.push_back(RTR("TiB")); + prefixes.push_back(RTR("PiB")); + prefixes.push_back(RTR("EiB")); + int prefix_idx = 0; - while (p_size > (_div * 1024) && prefix[prefix_idx][0]) { + while (prefix_idx < prefixes.size() && p_size > (_div * 1024)) { _div *= 1024; prefix_idx++; } - int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; - double divisor = prefix_idx > 0 ? _div : 1; + const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; + const double divisor = prefix_idx > 0 ? _div : 1; - return String::num(p_size / divisor, digits) + prefix[prefix_idx]; + return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; } bool String::is_abs_path() const { @@ -3278,7 +3422,7 @@ String String::http_unescape() const { if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { CharType ord2 = ord_at(i + 2); if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { - char bytes[2] = { (char)ord1, (char)ord2 }; + char bytes[3] = { (char)ord1, (char)ord2, 0 }; res += (char)strtol(bytes, NULL, 16); i += 2; } @@ -3467,7 +3611,7 @@ String String::xml_unescape() const { if (len == 0) return String(); str.resize(len + 1); - _xml_unescape(c_str(), l, &str[0]); + _xml_unescape(c_str(), l, str.ptrw()); str[len] = 0; return str; } @@ -3569,9 +3713,12 @@ bool String::is_valid_integer() const { bool String::is_valid_hex_number(bool p_with_prefix) const { - int from = 0; int len = length(); + if (len == 0) + return false; + + int from = 0; if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) from++; @@ -3726,6 +3873,20 @@ bool String::is_valid_html_color() const { return Color::html_is_valid(*this); } +bool String::is_valid_filename() const { + + String stripped = strip_edges(); + if (*this != stripped) { + return false; + } + + if (stripped == String()) { + return false; + } + + return !(find(":") != -1 || find("/") != -1 || find("\\") != -1 || find("?") != -1 || find("*") != -1 || find("\"") != -1 || find("|") != -1 || find("%") != -1 || find("<") != -1 || find(">") != -1); +} + bool String::is_valid_ip_address() const { if (find(":") >= 0) { @@ -3861,7 +4022,6 @@ String String::percent_decode() const { uint8_t a = LOWERCASE(cs[i + 1]); uint8_t b = LOWERCASE(cs[i + 2]); - c = 0; if (a >= '0' && a <= '9') c = (a - '0') << 4; else if (a >= 'a' && a <= 'f') @@ -4026,7 +4186,7 @@ String String::sprintf(const Array &values, bool *error) const { str = str.pad_decimals(min_decimals); // Show sign - if (show_sign && value >= 0) { + if (show_sign && str.left(1) != "-") { str = str.insert(0, "+"); } |