Skip to content

Commit 910133f

Browse files
committed
Merge pull request #104280 from Ivorforce/span-equality
Add `Span` equality (`==` and `!=`) operators.
2 parents 436853d + d7f5c13 commit 910133f

File tree

5 files changed

+44
-98
lines changed

5 files changed

+44
-98
lines changed

core/string/ustring.cpp

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -303,28 +303,8 @@ String &String::operator+=(char32_t p_char) {
303303
}
304304

305305
bool String::operator==(const char *p_str) const {
306-
// compare Latin-1 encoded c-string
307-
int len = strlen(p_str);
308-
309-
if (length() != len) {
310-
return false;
311-
}
312-
if (is_empty()) {
313-
return true;
314-
}
315-
316-
int l = length();
317-
318-
const char32_t *dst = get_data();
319-
320-
// Compare char by char
321-
for (int i = 0; i < l; i++) {
322-
if ((char32_t)p_str[i] != dst[i]) {
323-
return false;
324-
}
325-
}
326-
327-
return true;
306+
// Compare Latin-1 encoded c-string.
307+
return span() == Span(p_str, strlen(p_str)).reinterpret<uint8_t>();
328308
}
329309

330310
bool String::operator==(const wchar_t *p_str) const {
@@ -338,40 +318,16 @@ bool String::operator==(const wchar_t *p_str) const {
338318
}
339319

340320
bool String::operator==(const char32_t *p_str) const {
341-
const int len = strlen(p_str);
342-
343-
if (length() != len) {
344-
return false;
345-
}
346-
if (is_empty()) {
347-
return true;
348-
}
349-
350-
return memcmp(ptr(), p_str, len * sizeof(char32_t)) == 0;
321+
// Compare UTF-32 encoded c-string.
322+
return span() == Span(p_str, strlen(p_str));
351323
}
352324

353325
bool String::operator==(const String &p_str) const {
354-
if (length() != p_str.length()) {
355-
return false;
356-
}
357-
if (is_empty()) {
358-
return true;
359-
}
360-
361-
return memcmp(ptr(), p_str.ptr(), length() * sizeof(char32_t)) == 0;
326+
return span() == p_str.span();
362327
}
363328

364329
bool String::operator==(const Span<char32_t> &p_str_range) const {
365-
const int len = p_str_range.size();
366-
367-
if (length() != len) {
368-
return false;
369-
}
370-
if (is_empty()) {
371-
return true;
372-
}
373-
374-
return memcmp(ptr(), p_str_range.ptr(), len * sizeof(char32_t)) == 0;
330+
return span() == p_str_range;
375331
}
376332

377333
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) {
384340
return p_str == String::utf16((const char16_t *)p_chr);
385341
#else
386342
// wchar_t is 32-bi
387-
return p_str == String((const char32_t *)p_chr);
343+
return p_str == (const char32_t *)p_chr;
388344
#endif
389345
}
390346

core/string/ustring.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,7 @@ class [[nodiscard]] CharStringT {
211211
_FORCE_INLINE_ CharStringT(const T *p_cstr) { copy_from(p_cstr); }
212212
_FORCE_INLINE_ void operator=(const T *p_cstr) { copy_from(p_cstr); }
213213

214-
_FORCE_INLINE_ bool operator==(const CharStringT<T> &p_other) const {
215-
if (length() != p_other.length()) {
216-
return false;
217-
}
218-
return memcmp(ptr(), p_other.ptr(), length() * sizeof(T)) == 0;
219-
}
214+
_FORCE_INLINE_ bool operator==(const CharStringT<T> &p_other) const { return span() == p_other.span(); }
220215
_FORCE_INLINE_ bool operator!=(const CharStringT<T> &p_other) const { return !(*this == p_other); }
221216
_FORCE_INLINE_ bool operator<(const CharStringT<T> &p_other) const {
222217
if (length() == 0) {

core/templates/span.h

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@
3333
#include "core/error/error_macros.h"
3434
#include "core/typedefs.h"
3535

36+
template <typename LHS, typename RHS>
37+
bool are_spans_equal(const LHS *p_lhs, const RHS *p_rhs, size_t p_size) {
38+
if constexpr (std::is_same_v<LHS, RHS> && std::is_fundamental_v<LHS>) {
39+
// Optimize trivial type comparison.
40+
// is_trivially_equality_comparable would help, but it doesn't exist.
41+
return memcmp(p_lhs, p_rhs, p_size * sizeof(LHS)) == 0;
42+
} else {
43+
// Normal case: Need to iterate the array manually.
44+
for (size_t j = 0; j < p_size; j++) {
45+
if (p_lhs[j] != p_rhs[j]) {
46+
return false;
47+
}
48+
}
49+
50+
return true;
51+
}
52+
}
53+
3654
// Equivalent of std::span.
3755
// Represents a view into a contiguous memory space.
3856
// 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 {
126144
template <typename T>
127145
constexpr int64_t Span<T>::find_sequence(const Span<T> &p_span, uint64_t p_from) const {
128146
for (uint64_t i = p_from; i <= size() - p_span.size(); i++) {
129-
bool found = true;
130-
for (uint64_t j = 0; j < p_span.size(); j++) {
131-
if (ptr()[i + j] != p_span.ptr()[j]) {
132-
found = false;
133-
break;
134-
}
135-
}
136-
if (found) {
147+
if (are_spans_equal(ptr() + i, p_span.ptr(), p_span.size())) {
137148
return i;
138149
}
139150
}
@@ -154,14 +165,7 @@ constexpr int64_t Span<T>::rfind(const T &p_val, uint64_t p_from) const {
154165
template <typename T>
155166
constexpr int64_t Span<T>::rfind_sequence(const Span<T> &p_span, uint64_t p_from) const {
156167
for (int64_t i = p_from; i >= 0; i--) {
157-
bool found = true;
158-
for (uint64_t j = 0; j < p_span.size(); j++) {
159-
if (ptr()[i + j] != p_span.ptr()[j]) {
160-
found = false;
161-
break;
162-
}
163-
}
164-
if (found) {
168+
if (are_spans_equal(ptr() + i, p_span.ptr(), p_span.size())) {
165169
return i;
166170
}
167171
}
@@ -219,6 +223,16 @@ constexpr T Span<T>::max() const {
219223
return max_val;
220224
}
221225

226+
template <typename LHS, typename RHS>
227+
bool operator==(const Span<LHS> &p_lhs, const Span<RHS> &p_rhs) {
228+
return p_lhs.size() == p_rhs.size() && are_spans_equal(p_lhs.ptr(), p_rhs.ptr(), p_lhs.size());
229+
}
230+
231+
template <typename LHS, typename RHS>
232+
_FORCE_INLINE_ bool operator!=(const Span<LHS> &p_lhs, const Span<RHS> &p_rhs) {
233+
return !(p_lhs == p_rhs);
234+
}
235+
222236
// Zero-constructing Span initializes _ptr and _len to 0 (and thus empty).
223237
template <typename T>
224238
struct is_zero_constructible<Span<T>> : std::true_type {};

core/templates/vector.h

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -248,31 +248,8 @@ class Vector {
248248
return result;
249249
}
250250

251-
bool operator==(const Vector<T> &p_arr) const {
252-
Size s = size();
253-
if (s != p_arr.size()) {
254-
return false;
255-
}
256-
for (Size i = 0; i < s; i++) {
257-
if (operator[](i) != p_arr[i]) {
258-
return false;
259-
}
260-
}
261-
return true;
262-
}
263-
264-
bool operator!=(const Vector<T> &p_arr) const {
265-
Size s = size();
266-
if (s != p_arr.size()) {
267-
return true;
268-
}
269-
for (Size i = 0; i < s; i++) {
270-
if (operator[](i) != p_arr[i]) {
271-
return true;
272-
}
273-
}
274-
return false;
275-
}
251+
bool operator==(const Vector<T> &p_arr) const { return span() == p_arr.span(); }
252+
bool operator!=(const Vector<T> &p_arr) const { return span() != p_arr.span(); }
276253

277254
struct Iterator {
278255
_FORCE_INLINE_ T &operator*() const {

tests/core/templates/test_span.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ TEST_CASE("[Span] Constexpr Validators") {
6161
static_assert(span_string[0] == U'1');
6262
static_assert(span_string[span_string.size() - 1] == U'5');
6363

64+
CHECK_EQ(span_string, span_string); // Same identity / ptr.
65+
CHECK_EQ(span_string, Span(U"1223456", 6)); // Different ptr.
66+
CHECK_EQ(span_string, Span("122345").reinterpret<uint8_t>()); // Different type.
67+
6468
int idx = 0;
6569
for (const char32_t &chr : span_string) {
6670
CHECK_EQ(chr, span_string[idx++]);

0 commit comments

Comments
 (0)