From eb4038fa83009451d2ad9a1eda4cd6bcdf613958 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Wed, 22 Apr 2026 15:02:57 +0200 Subject: [PATCH] AK: Fix Utf16View::operator<=> code-unit ordering on little-endian The !has_ascii_storage() && !other.has_ascii_storage() branch did a byte-wise __builtin_memcmp over a char16_t array, which on little-endian does not give code-unit order: the low byte is compared first, so 0xD83D (bytes [0x3D, 0xD8]) spuriously compared less than 0x2764 (bytes [0x64, 0x27]) even though the code unit 0xD83D is greater. No in-tree caller currently uses operator<=> for Utf16View ordering, so this bug is dormant; the follow-up LibJS change exposes it. Replace the memcmp branch with a per-code-unit loop, which the compiler can auto-vectorize and which mirrors what is_code_unit_less_than already does. --- AK/Utf16View.h | 2 -- Tests/AK/TestUtf16View.cpp | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/AK/Utf16View.h b/AK/Utf16View.h index fadc5bd504..3660174a19 100644 --- a/AK/Utf16View.h +++ b/AK/Utf16View.h @@ -261,8 +261,6 @@ public: 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); diff --git a/Tests/AK/TestUtf16View.cpp b/Tests/AK/TestUtf16View.cpp index 1bcf606453..87759127bb 100644 --- a/Tests/AK/TestUtf16View.cpp +++ b/Tests/AK/TestUtf16View.cpp @@ -482,6 +482,16 @@ 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"ΓΏ"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)