AK: Implement the spaceship operator for UTF-16 strings

This commit is contained in:
Timothy Flynn 2025-08-02 19:24:20 -04:00 committed by Tim Flynn
commit 782f8c381c
Notes: github-actions[bot] 2025-08-05 11:08:44 +00:00
4 changed files with 98 additions and 0 deletions

View file

@ -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)

View file

@ -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()); }

View file

@ -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.

View file

@ -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);