diff options
Diffstat (limited to 'core/string/ustring.cpp')
-rw-r--r-- | core/string/ustring.cpp | 270 |
1 files changed, 167 insertions, 103 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index a24cff4f11..2b62b72a51 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -302,7 +302,7 @@ void String::copy_from(const char *p_cstr) { resize(len + 1); // include 0 - char32_t *dst = this->ptrw(); + char32_t *dst = ptrw(); for (size_t i = 0; i <= len; i++) { #if CHAR_MIN == 0 @@ -339,7 +339,7 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) { resize(len + 1); // include 0 - char32_t *dst = this->ptrw(); + char32_t *dst = ptrw(); for (int i = 0; i < len; i++) { #if CHAR_MIN == 0 @@ -927,52 +927,49 @@ static _FORCE_INLINE_ signed char natural_cmp_common(const char32_t *&r_this_str return 0; } -signed char String::naturalcasecmp_to(const String &p_str) const { - const char32_t *this_str = get_data(); - const char32_t *that_str = p_str.get_data(); - - if (this_str && that_str) { - while (*this_str == '.' || *that_str == '.') { - if (*this_str++ != '.') { +static _FORCE_INLINE_ signed char naturalcasecmp_to_base(const char32_t *p_this_str, const char32_t *p_that_str) { + if (p_this_str && p_that_str) { + while (*p_this_str == '.' || *p_that_str == '.') { + if (*p_this_str++ != '.') { return 1; } - if (*that_str++ != '.') { + if (*p_that_str++ != '.') { return -1; } - if (!*that_str) { + if (!*p_that_str) { return 1; } - if (!*this_str) { + if (!*p_this_str) { return -1; } } - while (*this_str) { - if (!*that_str) { + while (*p_this_str) { + if (!*p_that_str) { return 1; - } else if (is_digit(*this_str)) { - if (!is_digit(*that_str)) { + } else if (is_digit(*p_this_str)) { + if (!is_digit(*p_that_str)) { return -1; } - signed char ret = natural_cmp_common(this_str, that_str); + signed char ret = natural_cmp_common(p_this_str, p_that_str); if (ret) { return ret; } - } else if (is_digit(*that_str)) { + } else if (is_digit(*p_that_str)) { return 1; } else { - if (*this_str < *that_str) { // If current character in this is less, we are less. + if (*p_this_str < *p_that_str) { // If current character in this is less, we are less. return -1; - } else if (*this_str > *that_str) { // If current character in this is greater, we are greater. + } else if (*p_this_str > *p_that_str) { // If current character in this is greater, we are greater. return 1; } - this_str++; - that_str++; + p_this_str++; + p_that_str++; } } - if (*that_str) { + if (*p_that_str) { return -1; } } @@ -980,52 +977,56 @@ signed char String::naturalcasecmp_to(const String &p_str) const { return 0; } -signed char String::naturalnocasecmp_to(const String &p_str) const { +signed char String::naturalcasecmp_to(const String &p_str) const { const char32_t *this_str = get_data(); const char32_t *that_str = p_str.get_data(); - if (this_str && that_str) { - while (*this_str == '.' || *that_str == '.') { - if (*this_str++ != '.') { + return naturalcasecmp_to_base(this_str, that_str); +} + +static _FORCE_INLINE_ signed char naturalnocasecmp_to_base(const char32_t *p_this_str, const char32_t *p_that_str) { + if (p_this_str && p_that_str) { + while (*p_this_str == '.' || *p_that_str == '.') { + if (*p_this_str++ != '.') { return 1; } - if (*that_str++ != '.') { + if (*p_that_str++ != '.') { return -1; } - if (!*that_str) { + if (!*p_that_str) { return 1; } - if (!*this_str) { + if (!*p_this_str) { return -1; } } - while (*this_str) { - if (!*that_str) { + while (*p_this_str) { + if (!*p_that_str) { return 1; - } else if (is_digit(*this_str)) { - if (!is_digit(*that_str)) { + } else if (is_digit(*p_this_str)) { + if (!is_digit(*p_that_str)) { return -1; } - signed char ret = natural_cmp_common(this_str, that_str); + signed char ret = natural_cmp_common(p_this_str, p_that_str); if (ret) { return ret; } - } else if (is_digit(*that_str)) { + } else if (is_digit(*p_that_str)) { return 1; } else { - if (_find_upper(*this_str) < _find_upper(*that_str)) { // If current character in this is less, we are less. + if (_find_upper(*p_this_str) < _find_upper(*p_that_str)) { // If current character in this is less, we are less. return -1; - } else if (_find_upper(*this_str) > _find_upper(*that_str)) { // If current character in this is greater, we are greater. + } else if (_find_upper(*p_this_str) > _find_upper(*p_that_str)) { // If current character in this is greater, we are greater. return 1; } - this_str++; - that_str++; + p_this_str++; + p_that_str++; } } - if (*that_str) { + if (*p_that_str) { return -1; } } @@ -1033,6 +1034,53 @@ signed char String::naturalnocasecmp_to(const String &p_str) const { return 0; } +signed char String::naturalnocasecmp_to(const String &p_str) const { + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); + + return naturalnocasecmp_to_base(this_str, that_str); +} + +static _FORCE_INLINE_ signed char file_cmp_common(const char32_t *&r_this_str, const char32_t *&r_that_str) { + // Compare leading `_` sequences. + while ((*r_this_str == '_' && *r_that_str) || (*r_this_str && *r_that_str == '_')) { + // Sort `_` lower than everything except `.` + if (*r_this_str != '_') { + return *r_this_str == '.' ? -1 : 1; + } else if (*r_that_str != '_') { + return *r_that_str == '.' ? 1 : -1; + } + r_this_str++; + r_that_str++; + } + + return 0; +} + +signed char String::filecasecmp_to(const String &p_str) const { + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); + + signed char ret = file_cmp_common(this_str, that_str); + if (ret) { + return ret; + } + + return naturalcasecmp_to_base(this_str, that_str); +} + +signed char String::filenocasecmp_to(const String &p_str) const { + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); + + signed char ret = file_cmp_common(this_str, that_str); + if (ret) { + return ret; + } + + return naturalnocasecmp_to_base(this_str, that_str); +} + const char32_t *String::get_data() const { static const char32_t zero = 0; return size() ? &operator[](0) : &zero; @@ -1043,18 +1091,18 @@ String String::_camelcase_to_underscore() const { String new_string; int start_index = 0; - for (int i = 1; i < this->size(); i++) { - bool is_prev_upper = is_ascii_upper_case(cstr[i - 1]); - bool is_prev_lower = is_ascii_lower_case(cstr[i - 1]); + for (int i = 1; i < size(); i++) { + bool is_prev_upper = is_unicode_upper_case(cstr[i - 1]); + bool is_prev_lower = is_unicode_lower_case(cstr[i - 1]); bool is_prev_digit = is_digit(cstr[i - 1]); - bool is_curr_upper = is_ascii_upper_case(cstr[i]); - bool is_curr_lower = is_ascii_lower_case(cstr[i]); + bool is_curr_upper = is_unicode_upper_case(cstr[i]); + bool is_curr_lower = is_unicode_lower_case(cstr[i]); bool is_curr_digit = is_digit(cstr[i]); bool is_next_lower = false; - if (i + 1 < this->size()) { - is_next_lower = is_ascii_lower_case(cstr[i + 1]); + if (i + 1 < size()) { + is_next_lower = is_unicode_lower_case(cstr[i + 1]); } const bool cond_a = is_prev_lower && is_curr_upper; // aA @@ -1063,17 +1111,17 @@ String String::_camelcase_to_underscore() const { const bool cond_d = (is_prev_upper || is_prev_lower) && is_curr_digit; // A2, a2 if (cond_a || cond_b || cond_c || cond_d) { - new_string += this->substr(start_index, i - start_index) + "_"; + new_string += substr(start_index, i - start_index) + "_"; start_index = i; } } - new_string += this->substr(start_index, this->size() - start_index); + new_string += substr(start_index, size() - start_index); return new_string.to_lower(); } String String::capitalize() const { - String aux = this->_camelcase_to_underscore().replace("_", " ").strip_edges(); + String aux = _camelcase_to_underscore().replace("_", " ").strip_edges(); String cap; for (int i = 0; i < aux.get_slice_count(" "); i++) { String slice = aux.get_slicec(' ', i); @@ -1090,7 +1138,7 @@ String String::capitalize() const { } String String::to_camel_case() const { - String s = this->to_pascal_case(); + String s = to_pascal_case(); if (!s.is_empty()) { s[0] = _find_lower(s[0]); } @@ -1098,11 +1146,11 @@ String String::to_camel_case() const { } String String::to_pascal_case() const { - return this->capitalize().replace(" ", ""); + return capitalize().replace(" ", ""); } String String::to_snake_case() const { - return this->_camelcase_to_underscore().replace(" ", "_").strip_edges(); + return _camelcase_to_underscore().replace(" ", "_").strip_edges(); } String String::get_with_code_lines() const { @@ -1117,7 +1165,7 @@ String String::get_with_code_lines() const { return ret; } -int String::get_slice_count(String p_splitter) const { +int String::get_slice_count(const String &p_splitter) const { if (is_empty()) { return 0; } @@ -1136,7 +1184,7 @@ int String::get_slice_count(String p_splitter) const { return slices; } -String String::get_slice(String p_splitter, int p_slice) const { +String String::get_slice(const String &p_splitter, int p_slice) const { if (is_empty() || p_splitter.is_empty()) { return ""; } @@ -1185,7 +1233,7 @@ String String::get_slicec(char32_t p_splitter, int p_slice) const { return String(); } - const char32_t *c = this->ptr(); + const char32_t *c = ptr(); int i = 0; int prev = 0; int count = 0; @@ -1438,7 +1486,7 @@ Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allo return ret; } -String String::join(Vector<String> parts) const { +String String::join(const Vector<String> &parts) const { String ret; for (int i = 0; i < parts.size(); ++i) { if (i > 0) { @@ -1536,7 +1584,7 @@ String String::num(double p_num, int p_decimals) { fmt[5] = 'f'; fmt[6] = 0; } - // if we want to convert a double with as much decimal places as as + // if we want to convert a double with as much decimal places as // DBL_MAX or DBL_MIN then we would theoretically need a buffer of at least // DBL_MAX_10_EXP + 2 for DBL_MAX and DBL_MAX_10_EXP + 4 for DBL_MIN. // BUT those values where still giving me exceptions, so I tested from @@ -1822,7 +1870,7 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { bool decode_failed = false; { const char *ptrtmp = p_utf8; - const char *ptrtmp_limit = &p_utf8[p_len]; + const char *ptrtmp_limit = p_len >= 0 ? &p_utf8[p_len] : nullptr; int skip = 0; uint8_t c_start = 0; while (ptrtmp != ptrtmp_limit && *ptrtmp) { @@ -2062,12 +2110,12 @@ CharString String::utf8() const { String String::utf16(const char16_t *p_utf16, int p_len) { String ret; - ret.parse_utf16(p_utf16, p_len); + ret.parse_utf16(p_utf16, p_len, true); return ret; } -Error String::parse_utf16(const char16_t *p_utf16, int p_len) { +Error String::parse_utf16(const char16_t *p_utf16, int p_len, bool p_default_little_endian) { if (!p_utf16) { return ERR_INVALID_DATA; } @@ -2077,8 +2125,12 @@ Error String::parse_utf16(const char16_t *p_utf16, int p_len) { int cstr_size = 0; int str_size = 0; +#ifdef BIG_ENDIAN_ENABLED + bool byteswap = p_default_little_endian; +#else + bool byteswap = !p_default_little_endian; +#endif /* 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 @@ -2099,7 +2151,7 @@ Error String::parse_utf16(const char16_t *p_utf16, int p_len) { bool decode_error = false; { const char16_t *ptrtmp = p_utf16; - const char16_t *ptrtmp_limit = &p_utf16[p_len]; + const char16_t *ptrtmp_limit = p_len >= 0 ? &p_utf16[p_len] : nullptr; uint32_t c_prev = 0; bool skip = false; while (ptrtmp != ptrtmp_limit && *ptrtmp) { @@ -2459,7 +2511,7 @@ bool String::is_numeric() const { return true; // TODO: Use the parser below for this instead } -template <class C> +template <typename C> static double built_in_strtod( /* A decimal ASCII floating-point number, * optionally preceded by white space. Must @@ -3329,10 +3381,14 @@ bool String::begins_with(const String &p_string) const { bool String::begins_with(const char *p_string) const { int l = length(); - if (l == 0 || !p_string) { + if (!p_string) { return false; } + if (l == 0) { + return *p_string == 0; + } + const char32_t *str = &operator[](0); int i = 0; @@ -3515,8 +3571,8 @@ bool String::matchn(const String &p_wildcard) const { return _wildcard_match(p_wildcard.get_data(), get_data(), false); } -String String::format(const Variant &values, String placeholder) const { - String new_string = String(this->ptr()); +String String::format(const Variant &values, const String &placeholder) const { + String new_string = String(ptr()); if (values.get_type() == Variant::ARRAY) { Array values_arr = values; @@ -3961,27 +4017,42 @@ static int _humanize_digits(int p_num) { } String String::humanize_size(uint64_t p_size) { + int magnitude = 0; uint64_t _div = 1; - Vector<String> prefixes; - prefixes.push_back(RTR("B")); - prefixes.push_back(RTR("KiB")); - prefixes.push_back(RTR("MiB")); - prefixes.push_back(RTR("GiB")); - prefixes.push_back(RTR("TiB")); - prefixes.push_back(RTR("PiB")); - prefixes.push_back(RTR("EiB")); - - int prefix_idx = 0; - - while (prefix_idx < prefixes.size() - 1 && p_size > (_div * 1024)) { + while (p_size > _div * 1024 && magnitude < 6) { _div *= 1024; - prefix_idx++; + magnitude++; } - const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; - const double divisor = prefix_idx > 0 ? _div : 1; + if (magnitude == 0) { + return String::num(p_size) + " " + RTR("B"); + } else { + String suffix; + switch (magnitude) { + case 1: + suffix = RTR("KiB"); + break; + case 2: + suffix = RTR("MiB"); + break; + case 3: + suffix = RTR("GiB"); + break; + case 4: + suffix = RTR("TiB"); + break; + case 5: + suffix = RTR("PiB"); + break; + case 6: + suffix = RTR("EiB"); + break; + } - return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; + const double divisor = _div; + const int digits = _humanize_digits(p_size / _div); + return String::num(p_size / divisor).pad_decimals(digits) + " " + suffix; + } } bool String::is_absolute_path() const { @@ -4452,7 +4523,7 @@ bool String::is_valid_float() const { String String::path_to_file(const String &p_path) const { // Don't get base dir for src, this is expected to be a dir already. - String src = this->replace("\\", "/"); + String src = replace("\\", "/"); String dst = p_path.replace("\\", "/").get_base_dir(); String rel = src.path_to(dst); if (rel == dst) { // failed @@ -4463,7 +4534,7 @@ String String::path_to_file(const String &p_path) const { } String String::path_to(const String &p_path) const { - String src = this->replace("\\", "/"); + String src = replace("\\", "/"); String dst = p_path.replace("\\", "/"); if (!src.ends_with("/")) { src += "/"; @@ -4569,7 +4640,7 @@ bool String::is_valid_ip_address() const { if (find(":") >= 0) { Vector<String> ip = split(":"); for (int i = 0; i < ip.size(); i++) { - String n = ip[i]; + const String &n = ip[i]; if (n.is_empty()) { continue; } @@ -4591,7 +4662,7 @@ bool String::is_valid_ip_address() const { return false; } for (int i = 0; i < ip.size(); i++) { - String n = ip[i]; + const String &n = ip[i]; if (!n.is_valid_int()) { return false; } @@ -5208,7 +5279,7 @@ String String::sprintf(const Array &values, bool *error) const { return formatted; } -String String::quote(String quotechar) const { +String String::quote(const String "echar) const { return quotechar + *this + quotechar; } @@ -5376,9 +5447,7 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St /** * "Run-time TRanslate". Performs string replacement for internationalization - * within a running project. The translation string must be supplied by the - * project, as Godot does not provide built-in translations for `RTR()` strings - * to keep binary size low. A translation context can optionally be specified to + * without the editor. A translation context can optionally be specified to * disambiguate between identical source strings in translations. When * placeholders are desired, use `vformat(RTR("Example: %s"), some_string)`. * If a string mentions a quantity (and may therefore need a dynamic plural form), @@ -5392,9 +5461,8 @@ String RTR(const String &p_text, const String &p_context) { String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context); if (rtr.is_empty() || rtr == p_text) { return TranslationServer::get_singleton()->translate(p_text, p_context); - } else { - return rtr; } + return rtr; } return p_text; @@ -5402,13 +5470,10 @@ String RTR(const String &p_text, const String &p_context) { /** * "Run-time TRanslate for N items". Performs string replacement for - * internationalization within a running project. The translation string must be - * supplied by the project, as Godot does not provide built-in translations for - * `RTRN()` strings to keep binary size low. A translation context can - * optionally be specified to disambiguate between identical source strings in - * translations. Use `RTR()` if the string doesn't need dynamic plural form. - * When placeholders are desired, use - * `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`. + * internationalization without the editor. A translation context can optionally + * be specified to disambiguate between identical source strings in translations. + * Use `RTR()` if the string doesn't need dynamic plural form. When placeholders + * are desired, use `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`. * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`. * * NOTE: Do not use `RTRN()` in editor-only code (typically within the `editor/` @@ -5419,9 +5484,8 @@ String RTRN(const String &p_text, const String &p_text_plural, int p_n, const St String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context); if (rtr.is_empty() || 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 rtr; } // Return message based on English plural rule if translation is not possible. |