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.cpp804
1 files changed, 703 insertions, 101 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 2b62b72a51..2683addd4b 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -36,7 +36,7 @@
#include "core/os/memory.h"
#include "core/string/print_string.h"
#include "core/string/string_name.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/string/ucaps.h"
#include "core/variant/variant.h"
#include "core/version_generated.gen.h"
@@ -1184,6 +1184,26 @@ int String::get_slice_count(const String &p_splitter) const {
return slices;
}
+int String::get_slice_count(const char *p_splitter) const {
+ if (is_empty()) {
+ return 0;
+ }
+ if (p_splitter == nullptr || *p_splitter == '\0') {
+ return 0;
+ }
+
+ int pos = 0;
+ int slices = 1;
+ int splitter_length = strlen(p_splitter);
+
+ while ((pos = find(p_splitter, pos)) >= 0) {
+ slices++;
+ pos += splitter_length;
+ }
+
+ return slices;
+}
+
String String::get_slice(const String &p_splitter, int p_slice) const {
if (is_empty() || p_splitter.is_empty()) {
return "";
@@ -1224,6 +1244,47 @@ String String::get_slice(const String &p_splitter, int p_slice) const {
return ""; //no find!
}
+String String::get_slice(const char *p_splitter, int p_slice) const {
+ if (is_empty() || p_splitter == nullptr || *p_splitter == '\0') {
+ return "";
+ }
+
+ int pos = 0;
+ int prev_pos = 0;
+ //int slices=1;
+ if (p_slice < 0) {
+ return "";
+ }
+ if (find(p_splitter) == -1) {
+ return *this;
+ }
+
+ int i = 0;
+ int splitter_length = strlen(p_splitter);
+ while (true) {
+ pos = find(p_splitter, pos);
+ if (pos == -1) {
+ pos = length(); //reached end
+ }
+
+ int from = prev_pos;
+ //int to=pos;
+
+ if (p_slice == i) {
+ return substr(from, pos - from);
+ }
+
+ if (pos == length()) { //reached end and no find
+ break;
+ }
+ pos += splitter_length;
+ prev_pos = pos;
+ i++;
+ }
+
+ return ""; //no find!
+}
+
String String::get_slicec(char32_t p_splitter, int p_slice) const {
if (is_empty()) {
return String();
@@ -1338,6 +1399,54 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p
return ret;
}
+Vector<String> String::split(const char *p_splitter, bool p_allow_empty, int p_maxsplit) const {
+ Vector<String> ret;
+
+ if (is_empty()) {
+ if (p_allow_empty) {
+ ret.push_back("");
+ }
+ return ret;
+ }
+
+ int from = 0;
+ int len = length();
+
+ while (true) {
+ int end;
+ if (p_splitter == nullptr || *p_splitter == '\0') {
+ end = from + 1;
+ } else {
+ end = find(p_splitter, from);
+ if (end < 0) {
+ end = len;
+ }
+ }
+ if (p_allow_empty || (end > from)) {
+ if (p_maxsplit <= 0) {
+ ret.push_back(substr(from, end - from));
+ } else {
+ // 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));
+ }
+ }
+
+ if (end == len) {
+ break;
+ }
+
+ from = end + strlen(p_splitter);
+ }
+
+ return ret;
+}
+
Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const {
Vector<String> ret;
const int len = length();
@@ -1380,18 +1489,64 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int
return ret;
}
+Vector<String> String::rsplit(const char *p_splitter, bool p_allow_empty, int p_maxsplit) const {
+ Vector<String> ret;
+ const int len = length();
+ const int splitter_length = strlen(p_splitter);
+ int remaining_len = len;
+
+ while (true) {
+ if (remaining_len < 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));
+ }
+ break;
+ }
+
+ int left_edge;
+ if (p_splitter == nullptr || *p_splitter == '\0') {
+ left_edge = remaining_len - 1;
+ if (left_edge == 0) {
+ left_edge--; // Skip to the < 0 condition.
+ }
+ } else {
+ left_edge = rfind(p_splitter, remaining_len - splitter_length);
+ }
+
+ if (left_edge < 0) {
+ // no more splitters, we're done
+ ret.push_back(substr(0, remaining_len));
+ break;
+ }
+
+ int substr_start = left_edge + splitter_length;
+ if (p_allow_empty || substr_start < remaining_len) {
+ ret.push_back(substr(substr_start, remaining_len - substr_start));
+ }
+
+ remaining_len = left_edge;
+ }
+
+ ret.reverse();
+ return ret;
+}
+
Vector<double> String::split_floats(const String &p_splitter, bool p_allow_empty) const {
Vector<double> ret;
int from = 0;
int len = length();
+ String buffer = *this;
while (true) {
int end = find(p_splitter, from);
if (end < 0) {
end = len;
}
if (p_allow_empty || (end > from)) {
- ret.push_back(String::to_float(&get_data()[from]));
+ buffer[end] = 0;
+ ret.push_back(String::to_float(&buffer.get_data()[from]));
+ buffer[end] = _cowdata.get(end);
}
if (end == len) {
@@ -1409,6 +1564,7 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
int from = 0;
int len = length();
+ String buffer = *this;
while (true) {
int idx;
int end = findmk(p_splitters, from, &idx);
@@ -1420,7 +1576,9 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
}
if (p_allow_empty || (end > from)) {
- ret.push_back(String::to_float(&get_data()[from]));
+ buffer[end] = 0;
+ ret.push_back(String::to_float(&buffer.get_data()[from]));
+ buffer[end] = _cowdata.get(end);
}
if (end == len) {
@@ -1487,13 +1645,43 @@ Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allo
}
String String::join(const Vector<String> &parts) const {
+ if (parts.is_empty()) {
+ return String();
+ } else if (parts.size() == 1) {
+ return parts[0];
+ }
+
+ const int this_length = length();
+
+ int new_size = (parts.size() - 1) * this_length;
+ for (const String &part : parts) {
+ new_size += part.length();
+ }
+ new_size += 1;
+
String ret;
- for (int i = 0; i < parts.size(); ++i) {
- if (i > 0) {
- ret += *this;
+ ret.resize(new_size);
+ char32_t *ret_ptrw = ret.ptrw();
+ const char32_t *this_ptr = ptr();
+
+ bool first = true;
+ for (const String &part : parts) {
+ if (first) {
+ first = false;
+ } else if (this_length) {
+ memcpy(ret_ptrw, this_ptr, this_length * sizeof(char32_t));
+ ret_ptrw += this_length;
+ }
+
+ const int part_length = part.length();
+ if (part_length) {
+ memcpy(ret_ptrw, part.ptr(), part_length * sizeof(char32_t));
+ ret_ptrw += part_length;
}
- ret += parts[i];
}
+
+ *ret_ptrw = 0;
+
return ret;
}
@@ -1506,30 +1694,40 @@ char32_t String::char_lowercase(char32_t p_char) {
}
String String::to_upper() const {
- String upper = *this;
+ if (is_empty()) {
+ return *this;
+ }
- for (int i = 0; i < upper.size(); i++) {
- const char32_t s = upper[i];
- const char32_t t = _find_upper(s);
- if (s != t) { // avoid copy on write
- upper[i] = t;
- }
+ String upper;
+ upper.resize(size());
+ const char32_t *old_ptr = ptr();
+ char32_t *upper_ptrw = upper.ptrw();
+
+ while (*old_ptr) {
+ *upper_ptrw++ = _find_upper(*old_ptr++);
}
+ *upper_ptrw = 0;
+
return upper;
}
String String::to_lower() const {
- String lower = *this;
+ if (is_empty()) {
+ return *this;
+ }
- for (int i = 0; i < lower.size(); i++) {
- const char32_t s = lower[i];
- const char32_t t = _find_lower(s);
- if (s != t) { // avoid copy on write
- lower[i] = t;
- }
+ String lower;
+ lower.resize(size());
+ const char32_t *old_ptr = ptr();
+ char32_t *lower_ptrw = lower.ptrw();
+
+ while (*old_ptr) {
+ *lower_ptrw++ = _find_lower(*old_ptr++);
}
+ *lower_ptrw = 0;
+
return lower;
}
@@ -1767,15 +1965,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 };
+ ret.resize(p_len * 2 + 1);
+ char32_t *ret_ptrw = ret.ptrw();
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;
+ *ret_ptrw++ = hex[p_buffer[i] >> 4];
+ *ret_ptrw++ = hex[p_buffer[i] & 0xF];
}
+ *ret_ptrw = 0;
+
return ret;
}
@@ -1798,11 +1997,12 @@ Vector<uint8_t> String::hex_decode() const {
Vector<uint8_t> out;
int len = length() / 2;
out.resize(len);
+ uint8_t *out_ptrw = out.ptrw();
for (int i = 0; i < len; i++) {
char32_t c;
HEX_TO_BYTE(first, i * 2);
HEX_TO_BYTE(second, i * 2 + 1);
- out.write[i] = first * 16 + second;
+ out_ptrw[i] = first * 16 + second;
}
return out;
#undef HEX_TO_BYTE
@@ -1823,14 +2023,16 @@ CharString String::ascii(bool p_allow_extended) const {
CharString cs;
cs.resize(size());
+ char *cs_ptrw = cs.ptrw();
+ const char32_t *this_ptr = ptr();
for (int i = 0; i < size(); i++) {
- char32_t c = operator[](i);
+ char32_t c = this_ptr[i];
if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) {
- cs[i] = c;
+ cs_ptrw[i] = c;
} else {
print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1", (uint32_t)c));
- cs[i] = 0x20; // ascii doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane
+ cs_ptrw[i] = 0x20; // ASCII doesn't have a replacement character like unicode, 0x1a is sometimes used but is kinda arcane.
}
}
@@ -2963,8 +3165,9 @@ Vector<uint8_t> String::md5_buffer() const {
Vector<uint8_t> ret;
ret.resize(16);
+ uint8_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < 16; i++) {
- ret.write[i] = hash[i];
+ ret_ptrw[i] = hash[i];
}
return ret;
}
@@ -2976,8 +3179,9 @@ Vector<uint8_t> String::sha1_buffer() const {
Vector<uint8_t> ret;
ret.resize(20);
+ uint8_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < 20; i++) {
- ret.write[i] = hash[i];
+ ret_ptrw[i] = hash[i];
}
return ret;
@@ -2990,14 +3194,15 @@ Vector<uint8_t> String::sha256_buffer() const {
Vector<uint8_t> ret;
ret.resize(32);
+ uint8_t *ret_ptrw = ret.ptrw();
for (int i = 0; i < 32; i++) {
- ret.write[i] = hash[i];
+ ret_ptrw[i] = hash[i];
}
return ret;
}
String String::insert(int p_at_pos, const String &p_string) const {
- if (p_at_pos < 0) {
+ if (p_string.is_empty() || p_at_pos < 0) {
return *this;
}
@@ -3005,17 +3210,27 @@ String String::insert(int p_at_pos, const String &p_string) const {
p_at_pos = length();
}
- String pre;
+ String ret;
+ ret.resize(length() + p_string.length() + 1);
+ char32_t *ret_ptrw = ret.ptrw();
+ const char32_t *this_ptr = ptr();
+
if (p_at_pos > 0) {
- pre = substr(0, p_at_pos);
+ memcpy(ret_ptrw, this_ptr, p_at_pos * sizeof(char32_t));
+ ret_ptrw += p_at_pos;
}
- String post;
+ memcpy(ret_ptrw, p_string.ptr(), p_string.length() * sizeof(char32_t));
+ ret_ptrw += p_string.length();
+
if (p_at_pos < length()) {
- post = substr(p_at_pos, length() - p_at_pos);
+ memcpy(ret_ptrw, this_ptr + p_at_pos, (length() - p_at_pos) * sizeof(char32_t));
+ ret_ptrw += length() - p_at_pos;
}
- return pre + p_string + post;
+ *ret_ptrw = 0;
+
+ return ret;
}
String String::erase(int p_pos, int p_chars) const {
@@ -3087,23 +3302,20 @@ int String::find(const String &p_str, int p_from) const {
}
int String::find(const char *p_str, int p_from) const {
- if (p_from < 0) {
+ if (p_from < 0 || !p_str) {
return -1;
}
+ const int src_len = strlen(p_str);
+
const int len = length();
- if (len == 0) {
+ if (len == 0 || src_len == 0) {
return -1; // won't find anything!
}
const char32_t *src = get_data();
- int src_len = 0;
- while (p_str[src_len] != '\0') {
- src_len++;
- }
-
if (src_len == 1) {
const char32_t needle = p_str[0];
@@ -3238,6 +3450,46 @@ int String::findn(const String &p_str, int p_from) const {
return -1;
}
+int String::findn(const char *p_str, int p_from) const {
+ if (p_from < 0) {
+ return -1;
+ }
+
+ int src_len = strlen(p_str);
+
+ if (src_len == 0 || length() == 0) {
+ return -1; // won't find anything!
+ }
+
+ const char32_t *srcd = get_data();
+
+ 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;
+
+ if (read_pos >= length()) {
+ ERR_PRINT("read_pos>=length()");
+ return -1;
+ }
+
+ char32_t src = _find_lower(srcd[read_pos]);
+ char32_t dst = _find_lower(p_str[j]);
+
+ if (src != dst) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
int String::rfind(const String &p_str, int p_from) const {
// establish a limit
int limit = length() - p_str.length();
@@ -3285,6 +3537,57 @@ int String::rfind(const String &p_str, int p_from) const {
return -1;
}
+int String::rfind(const char *p_str, int p_from) const {
+ const int source_length = length();
+ int substring_length = strlen(p_str);
+
+ if (source_length == 0 || substring_length == 0) {
+ return -1; // won't find anything!
+ }
+
+ // establish a limit
+ int limit = length() - substring_length;
+ if (limit < 0) {
+ return -1;
+ }
+
+ // establish a starting point
+ int starting_point;
+ if (p_from < 0) {
+ starting_point = limit;
+ } else if (p_from > limit) {
+ starting_point = limit;
+ } else {
+ starting_point = p_from;
+ }
+
+ const char32_t *source = get_data();
+
+ for (int i = starting_point; i >= 0; i--) {
+ bool found = true;
+ for (int j = 0; j < substring_length; j++) {
+ int read_pos = i + j;
+
+ if (read_pos >= source_length) {
+ ERR_PRINT("read_pos>=source_length");
+ return -1;
+ }
+
+ const char32_t key_needle = p_str[j];
+ if (source[read_pos] != key_needle) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
int String::rfindn(const String &p_str, int p_from) const {
// establish a limit
int limit = length() - p_str.length();
@@ -3335,6 +3638,60 @@ int String::rfindn(const String &p_str, int p_from) const {
return -1;
}
+int String::rfindn(const char *p_str, int p_from) const {
+ const int source_length = length();
+ int substring_length = strlen(p_str);
+
+ if (source_length == 0 || substring_length == 0) {
+ return -1; // won't find anything!
+ }
+
+ // establish a limit
+ int limit = length() - substring_length;
+ if (limit < 0) {
+ return -1;
+ }
+
+ // establish a starting point
+ int starting_point;
+ if (p_from < 0) {
+ starting_point = limit;
+ } else if (p_from > limit) {
+ starting_point = limit;
+ } else {
+ starting_point = p_from;
+ }
+
+ const char32_t *source = get_data();
+
+ for (int i = starting_point; i >= 0; i--) {
+ bool found = true;
+ for (int j = 0; j < substring_length; j++) {
+ int read_pos = i + j;
+
+ if (read_pos >= source_length) {
+ ERR_PRINT("read_pos>=source_length");
+ return -1;
+ }
+
+ const char32_t key_needle = p_str[j];
+ int srcc = _find_lower(source[read_pos]);
+ int keyc = _find_lower(key_needle);
+
+ if (srcc != keyc) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
bool String::ends_with(const String &p_string) const {
int l = p_string.length();
if (l > length()) {
@@ -3357,6 +3714,31 @@ bool String::ends_with(const String &p_string) const {
return true;
}
+bool String::ends_with(const char *p_string) const {
+ if (!p_string) {
+ return false;
+ }
+
+ int l = strlen(p_string);
+ if (l > length()) {
+ return false;
+ }
+
+ if (l == 0) {
+ return true;
+ }
+
+ const char32_t *s = &operator[](length() - l);
+
+ for (int i = 0; i < l; i++) {
+ if (static_cast<char32_t>(p_string[i]) != s[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool String::begins_with(const String &p_string) const {
int l = p_string.length();
if (l > length()) {
@@ -3380,11 +3762,11 @@ bool String::begins_with(const String &p_string) const {
}
bool String::begins_with(const char *p_string) const {
- int l = length();
if (!p_string) {
return false;
}
+ int l = length();
if (l == 0) {
return *p_string == 0;
}
@@ -3456,14 +3838,61 @@ int String::_count(const String &p_string, int p_from, int p_to, bool p_case_ins
return c;
}
+int String::_count(const char *p_string, int p_from, int p_to, bool p_case_insensitive) const {
+ int substring_length = strlen(p_string);
+ if (substring_length == 0) {
+ return 0;
+ }
+ const int source_length = length();
+
+ if (source_length < substring_length) {
+ return 0;
+ }
+ String str;
+ int search_limit = p_to;
+ if (p_from >= 0 && p_to >= 0) {
+ if (p_to == 0) {
+ search_limit = source_length;
+ } else if (p_from >= p_to) {
+ return 0;
+ }
+ if (p_from == 0 && search_limit == source_length) {
+ str = String();
+ str.copy_from_unchecked(&get_data()[0], source_length);
+ } else {
+ str = substr(p_from, search_limit - 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 + substring_length, str.length() - substring_length);
+ ++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::count(const char *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);
}
+int String::countn(const char *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();
if (len == 0) {
@@ -3505,8 +3934,9 @@ Vector<String> String::bigrams() const {
return b;
}
b.resize(n_pairs);
+ String *b_ptrw = b.ptrw();
for (int i = 0; i < n_pairs; i++) {
- b.write[i] = substr(i, 2);
+ b_ptrw[i] = substr(i, 2);
}
return b;
}
@@ -3598,7 +4028,7 @@ String String::format(const Variant &values, const String &placeholder) const {
Variant v_val = values_arr[i];
String val = v_val;
- if (placeholder.find("_") > -1) {
+ if (placeholder.contains("_")) {
new_string = new_string.replace(placeholder.replace("_", i_as_str), val);
} else {
new_string = new_string.replace_first(placeholder, val);
@@ -3620,76 +4050,208 @@ String String::format(const Variant &values, const String &placeholder) const {
return new_string;
}
-String String::replace(const String &p_key, const String &p_with) const {
- String new_string;
+static String _replace_common(const String &p_this, const String &p_key, const String &p_with, bool p_case_insensitive) {
+ if (p_key.is_empty() || p_this.is_empty()) {
+ return p_this;
+ }
+
+ const int key_length = p_key.length();
+
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;
- search_from = result + p_key.length();
+ LocalVector<int> found;
+
+ while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
+ found.push_back(result);
+ search_from = result + key_length;
}
- if (search_from == 0) {
- return *this;
+ if (found.is_empty()) {
+ return p_this;
+ }
+
+ String new_string;
+
+ const int with_length = p_with.length();
+ const int old_length = p_this.length();
+
+ new_string.resize(old_length + found.size() * (with_length - key_length) + 1);
+
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = p_this.ptr();
+ const char32_t *with_ptr = p_with.ptr();
+
+ int last_pos = 0;
+
+ for (const int &pos : found) {
+ if (last_pos != pos) {
+ memcpy(new_ptrw, old_ptr + last_pos, (pos - last_pos) * sizeof(char32_t));
+ new_ptrw += (pos - last_pos);
+ }
+ if (with_length) {
+ memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t));
+ new_ptrw += with_length;
+ }
+ last_pos = pos + key_length;
+ }
+
+ if (last_pos != old_length) {
+ memcpy(new_ptrw, old_ptr + last_pos, (old_length - last_pos) * sizeof(char32_t));
+ new_ptrw += old_length - last_pos;
}
- new_string += substr(search_from, length() - search_from);
+ *new_ptrw = 0;
return new_string;
}
-String String::replace(const char *p_key, const char *p_with) const {
- String new_string;
+static String _replace_common(const String &p_this, char const *p_key, char const *p_with, bool p_case_insensitive) {
+ int key_length = strlen(p_key);
+
+ if (key_length == 0 || p_this.is_empty()) {
+ return p_this;
+ }
+
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;
- while (p_key[k] != '\0') {
- k++;
+ LocalVector<int> found;
+
+ while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
+ found.push_back(result);
+ search_from = result + key_length;
+ }
+
+ if (found.is_empty()) {
+ return p_this;
+ }
+
+ String new_string;
+
+ // Create string to speed up copying as we can't do `memcopy` between `char32_t` and `char`.
+ const String with_string(p_with);
+ const int with_length = with_string.length();
+ const int old_length = p_this.length();
+
+ new_string.resize(old_length + found.size() * (with_length - key_length) + 1);
+
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = p_this.ptr();
+ const char32_t *with_ptr = with_string.ptr();
+
+ int last_pos = 0;
+
+ for (const int &pos : found) {
+ if (last_pos != pos) {
+ memcpy(new_ptrw, old_ptr + last_pos, (pos - last_pos) * sizeof(char32_t));
+ new_ptrw += (pos - last_pos);
+ }
+ if (with_length) {
+ memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t));
+ new_ptrw += with_length;
}
- search_from = result + k;
+ last_pos = pos + key_length;
}
- if (search_from == 0) {
- return *this;
+ if (last_pos != old_length) {
+ memcpy(new_ptrw, old_ptr + last_pos, (old_length - last_pos) * sizeof(char32_t));
+ new_ptrw += old_length - last_pos;
}
- new_string += substr(search_from, length() - search_from);
+ *new_ptrw = 0;
return new_string;
}
+String String::replace(const String &p_key, const String &p_with) const {
+ return _replace_common(*this, p_key, p_with, false);
+}
+
+String String::replace(const char *p_key, const char *p_with) const {
+ return _replace_common(*this, p_key, p_with, false);
+}
+
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());
+ const int old_length = length();
+ const int key_length = p_key.length();
+ const int with_length = p_with.length();
+
+ String new_string;
+ new_string.resize(old_length + (with_length - key_length) + 1);
+
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = ptr();
+ const char32_t *with_ptr = p_with.ptr();
+
+ if (pos > 0) {
+ memcpy(new_ptrw, old_ptr, pos * sizeof(char32_t));
+ new_ptrw += pos;
+ }
+
+ if (with_length) {
+ memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t));
+ new_ptrw += with_length;
+ }
+ pos += key_length;
+
+ if (pos != old_length) {
+ memcpy(new_ptrw, old_ptr + pos, (old_length - pos) * sizeof(char32_t));
+ new_ptrw += (old_length - pos);
+ }
+
+ *new_ptrw = 0;
+
+ return new_string;
}
return *this;
}
-String String::replacen(const String &p_key, const String &p_with) const {
- String new_string;
- int search_from = 0;
- int result = 0;
+String String::replace_first(const char *p_key, const char *p_with) const {
+ int pos = find(p_key);
+ if (pos >= 0) {
+ const int old_length = length();
+ const int key_length = strlen(p_key);
+ const int with_length = strlen(p_with);
- 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();
- }
+ String new_string;
+ new_string.resize(old_length + (with_length - key_length) + 1);
- if (search_from == 0) {
- return *this;
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = ptr();
+
+ if (pos > 0) {
+ memcpy(new_ptrw, old_ptr, pos * sizeof(char32_t));
+ new_ptrw += pos;
+ }
+
+ for (int i = 0; i < with_length; ++i) {
+ *new_ptrw++ = p_with[i];
+ }
+ pos += key_length;
+
+ if (pos != old_length) {
+ memcpy(new_ptrw, old_ptr + pos, (old_length - pos) * sizeof(char32_t));
+ new_ptrw += (old_length - pos);
+ }
+
+ *new_ptrw = 0;
+
+ return new_string;
}
- new_string += substr(search_from, length() - search_from);
- return new_string;
+ return *this;
+}
+
+String String::replacen(const String &p_key, const String &p_with) const {
+ return _replace_common(*this, p_key, p_with, true);
+}
+
+String String::replacen(const char *p_key, const char *p_with) const {
+ return _replace_common(*this, p_key, p_with, true);
}
String String::repeat(int p_count) const {
@@ -3983,10 +4545,7 @@ String String::simplify_path() const {
dirs.remove_at(i);
i--;
} else if (d == "..") {
- if (i == 0) {
- dirs.remove_at(i);
- i--;
- } else {
+ if (i != 0) {
dirs.remove_at(i);
dirs.remove_at(i - 1);
i -= 2;
@@ -4065,7 +4624,7 @@ bool String::is_absolute_path() const {
}
}
-String String::validate_identifier() const {
+String String::validate_ascii_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier;
}
@@ -4088,7 +4647,7 @@ String String::validate_identifier() const {
return result;
}
-bool String::is_valid_identifier() const {
+bool String::is_valid_ascii_identifier() const {
int len = length();
if (len == 0) {
@@ -4110,6 +4669,26 @@ bool String::is_valid_identifier() const {
return true;
}
+bool String::is_valid_unicode_identifier() const {
+ const char32_t *str = ptr();
+ int len = length();
+
+ if (len == 0) {
+ return false; // Empty string.
+ }
+
+ if (!is_unicode_identifier_start(str[0])) {
+ return false;
+ }
+
+ for (int i = 1; i < len; i++) {
+ if (!is_unicode_identifier_continue(str[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool String::is_valid_string() const {
int l = length();
const char32_t *src = get_data();
@@ -4356,8 +4935,9 @@ String String::xml_unescape() const {
return String();
}
str.resize(len + 1);
- _xml_unescape(get_data(), l, str.ptrw());
- str[len] = 0;
+ char32_t *str_ptrw = str.ptrw();
+ _xml_unescape(get_data(), l, str_ptrw);
+ str_ptrw[len] = 0;
return str;
}
@@ -4420,6 +5000,15 @@ String String::trim_prefix(const String &p_prefix) const {
return s;
}
+String String::trim_prefix(const char *p_prefix) const {
+ String s = *this;
+ if (s.begins_with(p_prefix)) {
+ int prefix_length = strlen(p_prefix);
+ return s.substr(prefix_length, s.length() - prefix_length);
+ }
+ return s;
+}
+
String String::trim_suffix(const String &p_suffix) const {
String s = *this;
if (s.ends_with(p_suffix)) {
@@ -4428,6 +5017,14 @@ String String::trim_suffix(const String &p_suffix) const {
return s;
}
+String String::trim_suffix(const char *p_suffix) const {
+ String s = *this;
+ if (s.ends_with(p_suffix)) {
+ return s.substr(0, s.length() - strlen(p_suffix));
+ }
+ return s;
+}
+
bool String::is_valid_int() const {
int len = length();
@@ -4903,6 +5500,11 @@ String String::lpad(int min_length, const String &character) const {
// "fish %s %d pie" % ["frog", 12]
// In case of an error, the string returned is the error description and "error" is true.
String String::sprintf(const Array &values, bool *error) const {
+ static const String ZERO("0");
+ static const String SPACE(" ");
+ static const String MINUS("-");
+ static const String PLUS("+");
+
String formatted;
char32_t *self = (char32_t *)get_data();
bool in_format = false;
@@ -4925,7 +5527,7 @@ String String::sprintf(const Array &values, bool *error) const {
if (in_format) { // We have % - let's see what else we get.
switch (c) {
case '%': { // Replace %% with %
- formatted += chr(c);
+ formatted += c;
in_format = false;
break;
}
@@ -4975,7 +5577,7 @@ String String::sprintf(const Array &values, bool *error) const {
// Padding.
int pad_chars_count = (negative || show_sign) ? min_chars - 1 : min_chars;
- String pad_char = pad_with_zeros ? String("0") : String(" ");
+ const String &pad_char = pad_with_zeros ? ZERO : SPACE;
if (left_justified) {
str = str.rpad(pad_chars_count, pad_char);
} else {
@@ -4984,7 +5586,7 @@ String String::sprintf(const Array &values, bool *error) const {
// Sign.
if (show_sign || negative) {
- String sign_char = negative ? "-" : "+";
+ const String &sign_char = negative ? MINUS : PLUS;
if (left_justified) {
str = str.insert(0, sign_char);
} else {
@@ -5021,7 +5623,7 @@ String String::sprintf(const Array &values, bool *error) const {
// 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_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros
+ const String &pad_char = (pad_with_zeros && is_finite) ? ZERO : SPACE; // Never pad NaN or inf with zeros
if (left_justified) {
str = str.rpad(pad_chars_count, pad_char);
} else {
@@ -5030,7 +5632,7 @@ String String::sprintf(const Array &values, bool *error) const {
// Add sign if needed.
if (show_sign || is_negative) {
- String sign_char = is_negative ? "-" : "+";
+ const String &sign_char = is_negative ? MINUS : PLUS;
if (left_justified) {
str = str.insert(0, sign_char);
} else {
@@ -5083,7 +5685,7 @@ String String::sprintf(const Array &values, bool *error) const {
// Padding. Leave room for sign later if required.
int pad_chars_count = val < 0 ? min_chars - 1 : min_chars;
- String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros
+ const String &pad_char = (pad_with_zeros && is_finite) ? ZERO : SPACE; // Never pad NaN or inf with zeros
if (left_justified) {
number_str = number_str.rpad(pad_chars_count, pad_char);
} else {
@@ -5093,9 +5695,9 @@ String String::sprintf(const Array &values, bool *error) const {
// Add sign if needed.
if (val < 0) {
if (left_justified) {
- number_str = number_str.insert(0, "-");
+ number_str = number_str.insert(0, MINUS);
} else {
- number_str = number_str.insert(pad_with_zeros ? 0 : number_str.length() - initial_len, "-");
+ number_str = number_str.insert(pad_with_zeros ? 0 : number_str.length() - initial_len, MINUS);
}
}
@@ -5260,7 +5862,7 @@ String String::sprintf(const Array &values, bool *error) const {
in_decimals = false;
break;
default:
- formatted += chr(c);
+ formatted += c;
}
}
}