diff --git a/AK/Utf16FlyString.h b/AK/Utf16FlyString.h index aea83e3d849..ba130866b5d 100644 --- a/AK/Utf16FlyString.h +++ b/AK/Utf16FlyString.h @@ -95,6 +95,9 @@ public: [[nodiscard]] ALWAYS_INLINE bool operator==(Utf16View const& other) const { return m_data == other; } [[nodiscard]] ALWAYS_INLINE bool operator==(StringView other) const { return m_data == other; } + [[nodiscard]] ALWAYS_INLINE int operator<=>(Utf16FlyString const& other) const { return m_data.operator<=>(other.m_data); } + [[nodiscard]] ALWAYS_INLINE int operator<=>(Utf16View const& other) const { return m_data.operator<=>(other); } + [[nodiscard]] ALWAYS_INLINE bool equals_ignoring_ascii_case(Utf16FlyString const& other) const { if (*this == other) diff --git a/AK/Utf16StringBase.h b/AK/Utf16StringBase.h index 9d4ffdbd4ef..7d4c28938ba 100644 --- a/AK/Utf16StringBase.h +++ b/AK/Utf16StringBase.h @@ -151,6 +151,9 @@ public: [[nodiscard]] ALWAYS_INLINE bool operator==(Utf16View const& other) const { return utf16_view() == other; } [[nodiscard]] ALWAYS_INLINE bool operator==(StringView other) const { return utf16_view() == other; } + [[nodiscard]] ALWAYS_INLINE int operator<=>(Utf16StringBase const& other) const { return utf16_view().operator<=>(other.utf16_view()); } + [[nodiscard]] ALWAYS_INLINE int operator<=>(Utf16View const& other) const { return utf16_view().operator<=>(other); } + [[nodiscard]] ALWAYS_INLINE bool equals_ignoring_ascii_case(Utf16View const& other) const { return utf16_view().equals_ignoring_ascii_case(other); } [[nodiscard]] ALWAYS_INLINE bool equals_ignoring_ascii_case(Utf16StringBase const& other) const { return utf16_view().equals_ignoring_ascii_case(other.utf16_view()); } diff --git a/AK/Utf16View.h b/AK/Utf16View.h index 1b78520c644..c59cc536cf3 100644 --- a/AK/Utf16View.h +++ b/AK/Utf16View.h @@ -250,6 +250,45 @@ public: return this_it == end() && other_it == other_utf8.end(); } + [[nodiscard]] constexpr int operator<=>(Utf16View const& other) const + { + if (is_empty() && other.is_empty()) + return 0; + + size_t length = min(length_in_code_units(), other.length_in_code_units()); + int result = 0; + + if (has_ascii_storage() && other.has_ascii_storage()) { + result = __builtin_memcmp(m_string.ascii, other.m_string.ascii, length); + } else if (!has_ascii_storage() && !other.has_ascii_storage()) { + result = __builtin_memcmp(m_string.utf16, other.m_string.utf16, length * sizeof(char16_t)); + } else { + for (size_t i = 0; i < length; ++i) { + auto this_code_unit = code_unit_at(i); + auto other_code_unit = other.code_unit_at(i); + + if (this_code_unit < other_code_unit) { + result = -1; + break; + } + if (this_code_unit > other_code_unit) { + result = 1; + break; + } + } + } + + if (result == 0) { + if (length_in_code_units() == other.length_in_code_units()) + return 0; + if (length_in_code_units() < other.length_in_code_units()) + return -1; + return 1; + } + + return result; + } + [[nodiscard]] constexpr bool equals_ignoring_case(Utf16View const& other) const { // FIXME: Handle non-ASCII case insensitive comparisons. diff --git a/Tests/AK/TestUtf16View.cpp b/Tests/AK/TestUtf16View.cpp index fe1803259f8..3a7ed789b15 100644 --- a/Tests/AK/TestUtf16View.cpp +++ b/Tests/AK/TestUtf16View.cpp @@ -460,6 +460,59 @@ TEST_CASE(equals_utf8) EXPECT_NE(u"foo 😂 bar"sv, "foo 😀 bar"sv); } +TEST_CASE(comparison) +{ + EXPECT(!(u""sv < u""sv)); + EXPECT(!(u""sv > u""sv)); + EXPECT(u""sv <= u""sv); + EXPECT(u""sv >= u""sv); + + EXPECT(!(u"a"sv < u"a"sv)); + EXPECT(!(u"a"sv > u"a"sv)); + EXPECT(u"a"sv <= u"a"sv); + EXPECT(u"a"sv >= u"a"sv); + + EXPECT(!(u"😀"sv < u"😀"sv)); + EXPECT(!(u"😀"sv > u"😀"sv)); + EXPECT(u"😀"sv <= u"😀"sv); + EXPECT(u"😀"sv >= u"😀"sv); + + EXPECT(u"a"sv < u"b"sv); + EXPECT(!(u"a"sv > u"b"sv)); + EXPECT(u"a"sv <= u"b"sv); + EXPECT(!(u"a"sv >= u"b"sv)); + + EXPECT(Utf16View { "a"sv } < u"b"sv); + EXPECT(!(Utf16View { "a"sv } > u"b"sv)); + EXPECT(Utf16View { "a"sv } <= u"b"sv); + EXPECT(!(Utf16View { "a"sv } >= u"b"sv)); + + EXPECT(u"a"sv < u"aa"sv); + EXPECT(!(u"a"sv > u"aa"sv)); + EXPECT(u"a"sv <= u"aa"sv); + EXPECT(!(u"a"sv >= u"aa"sv)); + + EXPECT(Utf16View { "a"sv } < u"aa"sv); + EXPECT(!(Utf16View { "a"sv } > u"aa"sv)); + EXPECT(Utf16View { "a"sv } <= u"aa"sv); + EXPECT(!(Utf16View { "a"sv } >= u"aa"sv)); + + EXPECT(!(u"b"sv < u"a"sv)); + EXPECT(u"b"sv > u"a"sv); + EXPECT(!(u"b"sv <= u"a"sv)); + EXPECT(u"b"sv >= u"a"sv); + + EXPECT(u"😀"sv < u"😂"sv); + EXPECT(!(u"😀"sv > u"😂"sv)); + EXPECT(u"😀"sv <= u"😂"sv); + EXPECT(!(u"😀"sv >= u"😂"sv)); + + EXPECT(!(u"😂"sv < u"😀"sv)); + EXPECT(u"😂"sv > u"😀"sv); + EXPECT(!(u"😂"sv <= u"😀"sv)); + EXPECT(u"😂"sv >= u"😀"sv); +} + TEST_CASE(equals_ignoring_case) { auto string1 = Utf16String::from_utf8("foobar"sv);