Browse Source

AK: Define a traits helper for case-insensitive StringView hashing

Currently, we define a CaseInsensitiveStringTraits structure for String.
Using this structure for StringView involves allocating a String from
that view, and a second string to convert that intermediate string to
lowercase.

This defines CaseInsensitiveStringViewTraits (and the underlying helper
case_insensitive_string_hash) to avoid allocations.
Timothy Flynn 3 years ago
parent
commit
3dccaa39d8
3 changed files with 43 additions and 0 deletions
  1. 21 0
      AK/StringHash.h
  2. 10 0
      AK/StringView.h
  3. 12 0
      Tests/AK/TestStringView.cpp

+ 21 - 0
AK/StringHash.h

@@ -24,6 +24,27 @@ constexpr u32 string_hash(char const* characters, size_t length, u32 seed = 0)
     return hash;
 }
 
+constexpr u32 case_insensitive_string_hash(char const* characters, size_t length, u32 seed = 0)
+{
+    // AK/CharacterTypes.h cannot be included from here.
+    auto to_lowercase = [](char ch) -> u32 {
+        if (ch >= 'A' && ch <= 'Z')
+            return static_cast<u32>(ch) + 0x20;
+        return static_cast<u32>(ch);
+    };
+
+    u32 hash = seed;
+    for (size_t i = 0; i < length; ++i) {
+        hash += to_lowercase(characters[i]);
+        hash += (hash << 10);
+        hash ^= (hash >> 6);
+    }
+    hash += hash << 3;
+    hash ^= hash >> 11;
+    hash += hash << 15;
+    return hash;
+}
+
 }
 
 using AK::string_hash;

+ 10 - 0
AK/StringView.h

@@ -221,6 +221,15 @@ struct Traits<StringView> : public GenericTraits<StringView> {
     static unsigned hash(StringView s) { return s.hash(); }
 };
 
+struct CaseInsensitiveStringViewTraits : public Traits<StringView> {
+    static unsigned hash(StringView s)
+    {
+        if (s.is_empty())
+            return 0;
+        return case_insensitive_string_hash(s.characters_without_null_termination(), s.length());
+    }
+};
+
 }
 
 [[nodiscard]] ALWAYS_INLINE constexpr AK::StringView operator"" sv(const char* cstring, size_t length)
@@ -228,4 +237,5 @@ struct Traits<StringView> : public GenericTraits<StringView> {
     return AK::StringView(cstring, length);
 }
 
+using AK::CaseInsensitiveStringViewTraits;
 using AK::StringView;

+ 12 - 0
Tests/AK/TestStringView.cpp

@@ -189,3 +189,15 @@ TEST_CASE(constexpr_stuff)
     }
 #undef do_test
 }
+
+TEST_CASE(case_insensitive_hash)
+{
+    auto string1 = "abcdef"sv;
+    auto string2 = "ABCDEF"sv;
+    auto string3 = "aBcDeF"sv;
+    auto string4 = "foo"sv;
+
+    EXPECT_EQ(CaseInsensitiveStringViewTraits::hash(string1), CaseInsensitiveStringViewTraits::hash(string2));
+    EXPECT_EQ(CaseInsensitiveStringViewTraits::hash(string1), CaseInsensitiveStringViewTraits::hash(string3));
+    EXPECT_NE(CaseInsensitiveStringViewTraits::hash(string1), CaseInsensitiveStringViewTraits::hash(string4));
+}