1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-21 14:57:09 +00:00

Add Span equality (== and !=) operators.

Exchange duplicate equality iteration implementations across `Vector` and `String` with the `Span` version, for a speed boost.
This commit is contained in:
Lukas Tenbrink
2025-11-15 13:44:50 +01:00
parent ef34c3d534
commit d7f5c13db8
5 changed files with 44 additions and 98 deletions

View File

@@ -303,28 +303,8 @@ String &String::operator+=(char32_t p_char) {
} }
bool String::operator==(const char *p_str) const { bool String::operator==(const char *p_str) const {
// compare Latin-1 encoded c-string // Compare Latin-1 encoded c-string.
int len = strlen(p_str); return span() == Span(p_str, strlen(p_str)).reinterpret<uint8_t>();
if (length() != len) {
return false;
}
if (is_empty()) {
return true;
}
int l = length();
const char32_t *dst = get_data();
// Compare char by char
for (int i = 0; i < l; i++) {
if ((char32_t)p_str[i] != dst[i]) {
return false;
}
}
return true;
} }
bool String::operator==(const wchar_t *p_str) const { bool String::operator==(const wchar_t *p_str) const {
@@ -338,40 +318,16 @@ bool String::operator==(const wchar_t *p_str) const {
} }
bool String::operator==(const char32_t *p_str) const { bool String::operator==(const char32_t *p_str) const {
const int len = strlen(p_str); // Compare UTF-32 encoded c-string.
return span() == Span(p_str, strlen(p_str));
if (length() != len) {
return false;
}
if (is_empty()) {
return true;
}
return memcmp(ptr(), p_str, len * sizeof(char32_t)) == 0;
} }
bool String::operator==(const String &p_str) const { bool String::operator==(const String &p_str) const {
if (length() != p_str.length()) { return span() == p_str.span();
return false;
}
if (is_empty()) {
return true;
}
return memcmp(ptr(), p_str.ptr(), length() * sizeof(char32_t)) == 0;
} }
bool String::operator==(const Span<char32_t> &p_str_range) const { bool String::operator==(const Span<char32_t> &p_str_range) const {
const int len = p_str_range.size(); return span() == p_str_range;
if (length() != len) {
return false;
}
if (is_empty()) {
return true;
}
return memcmp(ptr(), p_str_range.ptr(), len * sizeof(char32_t)) == 0;
} }
bool operator==(const char *p_chr, const String &p_str) { bool operator==(const char *p_chr, const String &p_str) {
@@ -384,7 +340,7 @@ bool operator==(const wchar_t *p_chr, const String &p_str) {
return p_str == String::utf16((const char16_t *)p_chr); return p_str == String::utf16((const char16_t *)p_chr);
#else #else
// wchar_t is 32-bi // wchar_t is 32-bi
return p_str == String((const char32_t *)p_chr); return p_str == (const char32_t *)p_chr;
#endif #endif
} }

View File

@@ -211,12 +211,7 @@ public:
_FORCE_INLINE_ CharStringT(const T *p_cstr) { copy_from(p_cstr); } _FORCE_INLINE_ CharStringT(const T *p_cstr) { copy_from(p_cstr); }
_FORCE_INLINE_ void operator=(const T *p_cstr) { copy_from(p_cstr); } _FORCE_INLINE_ void operator=(const T *p_cstr) { copy_from(p_cstr); }
_FORCE_INLINE_ bool operator==(const CharStringT<T> &p_other) const { _FORCE_INLINE_ bool operator==(const CharStringT<T> &p_other) const { return span() == p_other.span(); }
if (length() != p_other.length()) {
return false;
}
return memcmp(ptr(), p_other.ptr(), length() * sizeof(T)) == 0;
}
_FORCE_INLINE_ bool operator!=(const CharStringT<T> &p_other) const { return !(*this == p_other); } _FORCE_INLINE_ bool operator!=(const CharStringT<T> &p_other) const { return !(*this == p_other); }
_FORCE_INLINE_ bool operator<(const CharStringT<T> &p_other) const { _FORCE_INLINE_ bool operator<(const CharStringT<T> &p_other) const {
if (length() == 0) { if (length() == 0) {

View File

@@ -33,6 +33,24 @@
#include "core/error/error_macros.h" #include "core/error/error_macros.h"
#include "core/typedefs.h" #include "core/typedefs.h"
template <typename LHS, typename RHS>
bool are_spans_equal(const LHS *p_lhs, const RHS *p_rhs, size_t p_size) {
if constexpr (std::is_same_v<LHS, RHS> && std::is_fundamental_v<LHS>) {
// Optimize trivial type comparison.
// is_trivially_equality_comparable would help, but it doesn't exist.
return memcmp(p_lhs, p_rhs, p_size * sizeof(LHS)) == 0;
} else {
// Normal case: Need to iterate the array manually.
for (size_t j = 0; j < p_size; j++) {
if (p_lhs[j] != p_rhs[j]) {
return false;
}
}
return true;
}
}
// Equivalent of std::span. // Equivalent of std::span.
// Represents a view into a contiguous memory space. // Represents a view into a contiguous memory space.
// DISCLAIMER: This data type does not own the underlying buffer. DO NOT STORE IT. // DISCLAIMER: This data type does not own the underlying buffer. DO NOT STORE IT.
@@ -126,14 +144,7 @@ constexpr int64_t Span<T>::find(const T &p_val, uint64_t p_from) const {
template <typename T> template <typename T>
constexpr int64_t Span<T>::find_sequence(const Span<T> &p_span, uint64_t p_from) const { constexpr int64_t Span<T>::find_sequence(const Span<T> &p_span, uint64_t p_from) const {
for (uint64_t i = p_from; i <= size() - p_span.size(); i++) { for (uint64_t i = p_from; i <= size() - p_span.size(); i++) {
bool found = true; if (are_spans_equal(ptr() + i, p_span.ptr(), p_span.size())) {
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 i;
} }
} }
@@ -154,14 +165,7 @@ constexpr int64_t Span<T>::rfind(const T &p_val, uint64_t p_from) const {
template <typename T> template <typename T>
constexpr int64_t Span<T>::rfind_sequence(const Span<T> &p_span, uint64_t p_from) const { constexpr int64_t Span<T>::rfind_sequence(const Span<T> &p_span, uint64_t p_from) const {
for (int64_t i = p_from; i >= 0; i--) { for (int64_t i = p_from; i >= 0; i--) {
bool found = true; if (are_spans_equal(ptr() + i, p_span.ptr(), p_span.size())) {
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 i;
} }
} }
@@ -219,6 +223,16 @@ constexpr T Span<T>::max() const {
return max_val; return max_val;
} }
template <typename LHS, typename RHS>
bool operator==(const Span<LHS> &p_lhs, const Span<RHS> &p_rhs) {
return p_lhs.size() == p_rhs.size() && are_spans_equal(p_lhs.ptr(), p_rhs.ptr(), p_lhs.size());
}
template <typename LHS, typename RHS>
_FORCE_INLINE_ bool operator!=(const Span<LHS> &p_lhs, const Span<RHS> &p_rhs) {
return !(p_lhs == p_rhs);
}
// Zero-constructing Span initializes _ptr and _len to 0 (and thus empty). // Zero-constructing Span initializes _ptr and _len to 0 (and thus empty).
template <typename T> template <typename T>
struct is_zero_constructible<Span<T>> : std::true_type {}; struct is_zero_constructible<Span<T>> : std::true_type {};

View File

@@ -248,31 +248,8 @@ public:
return result; return result;
} }
bool operator==(const Vector<T> &p_arr) const { bool operator==(const Vector<T> &p_arr) const { return span() == p_arr.span(); }
Size s = size(); bool operator!=(const Vector<T> &p_arr) const { return span() != p_arr.span(); }
if (s != p_arr.size()) {
return false;
}
for (Size i = 0; i < s; i++) {
if (operator[](i) != p_arr[i]) {
return false;
}
}
return true;
}
bool operator!=(const Vector<T> &p_arr) const {
Size s = size();
if (s != p_arr.size()) {
return true;
}
for (Size i = 0; i < s; i++) {
if (operator[](i) != p_arr[i]) {
return true;
}
}
return false;
}
struct Iterator { struct Iterator {
_FORCE_INLINE_ T &operator*() const { _FORCE_INLINE_ T &operator*() const {

View File

@@ -61,6 +61,10 @@ TEST_CASE("[Span] Constexpr Validators") {
static_assert(span_string[0] == U'1'); static_assert(span_string[0] == U'1');
static_assert(span_string[span_string.size() - 1] == U'5'); static_assert(span_string[span_string.size() - 1] == U'5');
CHECK_EQ(span_string, span_string); // Same identity / ptr.
CHECK_EQ(span_string, Span(U"1223456", 6)); // Different ptr.
CHECK_EQ(span_string, Span("122345").reinterpret<uint8_t>()); // Different type.
int idx = 0; int idx = 0;
for (const char32_t &chr : span_string) { for (const char32_t &chr : span_string) {
CHECK_EQ(chr, span_string[idx++]); CHECK_EQ(chr, span_string[idx++]);