diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 340ba63c769..0cb4b2c414a 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3055,69 +3055,42 @@ String String::substr(int p_from, int p_chars) const { } int String::find(const String &p_str, int p_from) const { - if (p_from < 0) { - return -1; - } - - const int src_len = p_str.length(); - + const int str_len = p_str.length(); const int len = length(); - if (src_len == 0 || len == 0) { - return -1; // won't find anything! + if (p_from < 0) { + p_from = len - str_len + p_from + 1; + } + if (p_from < 0 || p_from > len - str_len || p_str.is_empty()) { + return -1; // Still out of bounds } - if (src_len == 1) { - return find_char(p_str[0], p_from); // Optimize with single-char find. + if (p_str.length() == 1) { + // Optimize with single-char implementation. + return span().find(p_str[0], p_from); } - const char32_t *src = get_data(); - const char32_t *str = p_str.get_data(); - - for (int i = p_from; i <= (len - src_len); i++) { - bool found = true; - for (int j = 0; j < src_len; j++) { - int read_pos = i + j; - - if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); - return -1; - } - - if (src[read_pos] != str[j]) { - found = false; - break; - } - } - - if (found) { - return i; - } - } - - return -1; + return span().find_sequence(p_str.span(), p_from); } int String::find(const char *p_str, int p_from) const { - if (p_from < 0 || !p_str) { - return -1; - } - - const int src_len = strlen(p_str); - + const int str_len = strlen(p_str); const int len = length(); - if (len == 0 || src_len == 0) { - return -1; // won't find anything! + if (p_from < 0) { + p_from = len - str_len + p_from + 1; + } + if (p_from < 0 || p_from > len - str_len || str_len == 0) { + return -1; // Still out of bounds } - if (src_len == 1) { + if (str_len == 1) { return find_char(*p_str, p_from); // Optimize with single-char find. } const char32_t *src = get_data(); - if (src_len == 1) { + if (str_len == 1) { const char32_t needle = p_str[0]; for (int i = p_from; i < len; i++) { @@ -3127,13 +3100,13 @@ int String::find(const char *p_str, int p_from) const { } } else { - for (int i = p_from; i <= (len - src_len); i++) { + for (int i = p_from; i <= (len - str_len); i++) { bool found = true; - for (int j = 0; j < src_len; j++) { + for (int j = 0; j < str_len; j++) { int read_pos = i + j; if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); + ERR_PRINT("read_pos>=length()"); return -1; } @@ -3170,7 +3143,7 @@ int String::findmk(const Vector &p_keys, int p_from, int *r_key) const { return -1; } - //int src_len=p_str.length(); + //int str_len=p_str.length(); const String *keys = &p_keys[0]; int key_count = p_keys.size(); int len = length(); @@ -3218,24 +3191,24 @@ int String::findmk(const Vector &p_keys, int p_from, int *r_key) const { } int String::findn(const String &p_str, int p_from) const { + const int str_len = p_str.length(); + const int len = length(); + if (p_from < 0) { - return -1; + p_from = len - str_len + p_from + 1; } - - int src_len = p_str.length(); - - if (src_len == 0 || length() == 0) { - return -1; // won't find anything! + if (p_from < 0 || p_from > len - str_len || p_str.is_empty()) { + return -1; // Still out of bounds } const char32_t *srcd = get_data(); - for (int i = p_from; i <= (length() - src_len); i++) { + for (int i = p_from; i <= (len - str_len); i++) { bool found = true; - for (int j = 0; j < src_len; j++) { + for (int j = 0; j < str_len; j++) { int read_pos = i + j; - if (read_pos >= length()) { + if (read_pos >= len) { ERR_PRINT("read_pos>=length()"); return -1; } @@ -3258,24 +3231,24 @@ int String::findn(const String &p_str, int p_from) const { } int String::findn(const char *p_str, int p_from) const { + const int str_len = strlen(p_str); + const int len = length(); + if (p_from < 0) { - return -1; + p_from = len - str_len + p_from + 1; } - - int src_len = strlen(p_str); - - if (src_len == 0 || length() == 0) { - return -1; // won't find anything! + if (p_from < 0 || p_from > len - str_len || str_len == 0) { + return -1; // Still out of bounds } const char32_t *srcd = get_data(); - for (int i = p_from; i <= (length() - src_len); i++) { + for (int i = p_from; i <= (len - str_len); i++) { bool found = true; - for (int j = 0; j < src_len; j++) { + for (int j = 0; j < str_len; j++) { int read_pos = i + j; - if (read_pos >= length()) { + if (read_pos >= len) { ERR_PRINT("read_pos>=length()"); return -1; } @@ -3298,85 +3271,44 @@ int String::findn(const char *p_str, int p_from) const { } int String::rfind(const String &p_str, int p_from) const { - // establish a limit - int limit = length() - p_str.length(); - if (limit < 0) { - return -1; - } + const int str_len = p_str.length(); + const int len = length(); - // establish a starting point if (p_from < 0) { - p_from = limit; - } else if (p_from > limit) { - p_from = limit; + p_from = len - str_len + p_from + 1; + } + if (p_from < 0 || p_from > len - str_len || p_str.is_empty()) { + return -1; // Still out of bounds } - int src_len = p_str.length(); - int len = length(); - - if (src_len == 0 || len == 0) { - return -1; // won't find anything! + if (p_str.length() == 1) { + // Optimize with single-char implementation. + return span().rfind(p_str[0], p_from); } - const char32_t *src = get_data(); - - for (int i = p_from; i >= 0; i--) { - bool found = true; - for (int j = 0; j < src_len; j++) { - int read_pos = i + j; - - if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); - return -1; - } - - if (src[read_pos] != p_str[j]) { - found = false; - break; - } - } - - if (found) { - return i; - } - } - - return -1; + return span().rfind_sequence(p_str.span(), p_from); } int String::rfind(const char *p_str, int p_from) const { - const int source_length = length(); - int substring_length = strlen(p_str); + const int str_len = strlen(p_str); + const int len = length(); - 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; + p_from = len - str_len + p_from + 1; + } + if (p_from < 0 || p_from > len - str_len || str_len == 0) { + return -1; // Still out of bounds } const char32_t *source = get_data(); - for (int i = starting_point; i >= 0; i--) { + for (int i = p_from; i >= 0; i--) { bool found = true; - for (int j = 0; j < substring_length; j++) { + for (int j = 0; j < str_len; j++) { int read_pos = i + j; - if (read_pos >= source_length) { - ERR_PRINT("read_pos>=source_length"); + if (read_pos >= length()) { + ERR_PRINT("read_pos>=length()"); return -1; } @@ -3406,35 +3338,25 @@ int String::rfind_char(char32_t p_char, int p_from) const { } int String::rfindn(const String &p_str, int p_from) const { - // establish a limit - int limit = length() - p_str.length(); - if (limit < 0) { - return -1; - } + const int str_len = p_str.length(); + const int len = length(); - // establish a starting point if (p_from < 0) { - p_from = limit; - } else if (p_from > limit) { - p_from = limit; + p_from = len - str_len + p_from + 1; } - - int src_len = p_str.length(); - int len = length(); - - if (src_len == 0 || len == 0) { - return -1; // won't find anything! + if (p_from < 0 || p_from > len - str_len || p_str.is_empty()) { + return -1; // Still out of bounds } const char32_t *src = get_data(); for (int i = p_from; i >= 0; i--) { bool found = true; - for (int j = 0; j < src_len; j++) { + for (int j = 0; j < str_len; j++) { int read_pos = i + j; if (read_pos >= len) { - ERR_PRINT("read_pos>=len"); + ERR_PRINT("read_pos>=length()"); return -1; } @@ -3456,38 +3378,25 @@ int String::rfindn(const String &p_str, int p_from) const { } int String::rfindn(const char *p_str, int p_from) const { - const int source_length = length(); - int substring_length = strlen(p_str); + const int str_len = strlen(p_str); + const int len = length(); - 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; + p_from = len - str_len + p_from + 1; + } + if (p_from < 0 || p_from > len - str_len || str_len == 0) { + return -1; // Still out of bounds } const char32_t *source = get_data(); - for (int i = starting_point; i >= 0; i--) { + for (int i = p_from; i >= 0; i--) { bool found = true; - for (int j = 0; j < substring_length; j++) { + for (int j = 0; j < str_len; j++) { int read_pos = i + j; - if (read_pos >= source_length) { - ERR_PRINT("read_pos>=source_length"); + if (read_pos >= len) { + ERR_PRINT("read_pos>=length()"); return -1; } diff --git a/core/templates/span.h b/core/templates/span.h index d909e4264b5..0db7e47ed17 100644 --- a/core/templates/span.h +++ b/core/templates/span.h @@ -98,8 +98,11 @@ public: // Algorithms. constexpr int64_t find(const T &p_val, uint64_t p_from = 0) const; + constexpr int64_t find_sequence(const Span &p_span, uint64_t p_from = 0) const; constexpr int64_t rfind(const T &p_val, uint64_t p_from) const; _FORCE_INLINE_ constexpr int64_t rfind(const T &p_val) const { return rfind(p_val, size() - 1); } + constexpr int64_t rfind_sequence(const Span &p_span, uint64_t p_from) const; + _FORCE_INLINE_ constexpr int64_t rfind_sequence(const Span &p_span) const { return rfind_sequence(p_span, size() - p_span.size()); } constexpr uint64_t count(const T &p_val) const; /// Find the index of the given value using binary search. /// Note: Assumes that elements in the span are sorted. Otherwise, use find() instead. @@ -117,6 +120,24 @@ constexpr int64_t Span::find(const T &p_val, uint64_t p_from) const { return -1; } +template +constexpr int64_t Span::find_sequence(const Span &p_span, uint64_t p_from) const { + for (uint64_t i = p_from; i <= size() - p_span.size(); i++) { + bool found = true; + for (uint64_t j = 0; j < p_span.size(); j++) { + if (ptr()[i + j] != p_span.ptr()[j]) { + found = false; + break; + } + } + if (found) { + return i; + } + } + + return -1; +} + template constexpr int64_t Span::rfind(const T &p_val, uint64_t p_from) const { for (int64_t i = p_from; i >= 0; i--) { @@ -127,6 +148,24 @@ constexpr int64_t Span::rfind(const T &p_val, uint64_t p_from) const { return -1; } +template +constexpr int64_t Span::rfind_sequence(const Span &p_span, uint64_t p_from) const { + for (int64_t i = p_from; i >= 0; i--) { + bool found = true; + for (uint64_t j = 0; j < p_span.size(); j++) { + if (ptr()[i + j] != p_span.ptr()[j]) { + found = false; + break; + } + } + if (found) { + return i; + } + } + + return -1; +} + template constexpr uint64_t Span::count(const T &p_val) const { uint64_t amount = 0; diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 547f198dbc1..1cd24390696 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -233,6 +233,7 @@ [/csharp] [/codeblocks] [b]Note:[/b] If you just want to know whether the string contains [param what], use [method contains]. In GDScript, you may also use the [code]in[/code] operator. + [b]Note:[/b] A negative value of [param from] is converted to a starting index by counting back from the last possible index with enough space to find [param what]. @@ -829,6 +830,8 @@ Returns the index of the [b]last[/b] occurrence of [param what] in this string, or [code]-1[/code] if there are none. The search's start can be specified with [param from], continuing to the beginning of the string. This method is the reverse of [method find]. + [b]Note:[/b] A negative value of [param from] is converted to a starting index by counting back from the last possible index with enough space to find [param what]. + [b]Note:[/b] A value of [param from] that is greater than the last possible index with enough space to find [param what] is considered out-of-bounds, and returns [code]-1[/code]. diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index d49dc2ab258..98f48867ef7 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -215,6 +215,7 @@ [/csharp] [/codeblocks] [b]Note:[/b] If you just want to know whether the string contains [param what], use [method contains]. In GDScript, you may also use the [code]in[/code] operator. + [b]Note:[/b] A negative value of [param from] is converted to a starting index by counting back from the last possible index with enough space to find [param what]. @@ -736,6 +737,8 @@ Returns the index of the [b]last[/b] occurrence of [param what] in this string, or [code]-1[/code] if there are none. The search's start can be specified with [param from], continuing to the beginning of the string. This method is the reverse of [method find]. + [b]Note:[/b] A negative value of [param from] is converted to a starting index by counting back from the last possible index with enough space to find [param what]. + [b]Note:[/b] A value of [param from] that is greater than the last possible index with enough space to find [param what] is considered out-of-bounds, and returns [code]-1[/code].