summaryrefslogtreecommitdiffstats
path: root/core/string/ustring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/string/ustring.cpp')
-rw-r--r--core/string/ustring.cpp270
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 &quotechar) 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.