AK: Add and use IdentityHashTraits<Integral>

These new traits are identical to `Traits<Integral>`, except that
calling `.hash()` will return the value itself instead of hashing it.
This should be used in cases where either the value is already a proper
hash, or using the value as a hash will yield "good enough" performance
in e.g. HashTable.

Types larger than 32 bits are folded in on themselves. Collision tests
on some popular hashing algorithms show that XOR folding slightly
increases the number of collisions, but this allows `IdentityHashTraits`
not to make any assumptions on which bits are the most relevant for the
final hash.
This commit is contained in:
Jelle Raaijmakers
2026-02-23 15:33:06 +01:00
committed by Jelle Raaijmakers
parent 04719e6491
commit f175a00003
Notes: github-actions[bot] 2026-02-24 12:26:01 +00:00
4 changed files with 15 additions and 10 deletions

View File

@@ -95,7 +95,7 @@ private:
SearchableCircularBuffer(ByteBuffer);
HashMap<unsigned, size_t> m_hash_location_map;
HashMap<unsigned, size_t, IdentityHashTraits<unsigned>> m_hash_location_map;
HashMap<size_t, size_t> m_location_chain_map;
ErrorOr<void> insert_location_hash(ReadonlyBytes value, size_t raw_offset);

View File

@@ -83,9 +83,21 @@ struct Traits<T> : public DefaultTraits<T> {
static constexpr bool is_trivially_serializable() { return Traits<UnderlyingType<T>>::is_trivially_serializable(); }
};
template<Integral T>
struct IdentityHashTraits : public Traits<T> {
static constexpr unsigned hash(T value)
{
if constexpr (sizeof(T) <= 4)
return static_cast<unsigned>(value);
else
return static_cast<unsigned>(value ^ (value >> 32));
}
};
}
#if USING_AK_GLOBALLY
using AK::DefaultTraits;
using AK::IdentityHashTraits;
using AK::Traits;
#endif

View File

@@ -514,13 +514,6 @@ private:
Node* m_last { nullptr };
};
struct SufficientlyUniformValueTraits : DefaultTraits<u64> {
static constexpr unsigned hash(u64 value)
{
return (value >> 32) ^ value;
}
};
template<class Parser>
Matcher<Parser>::ExecuteResult Matcher<Parser>::execute(MatchInput const& input, MatchState& state, size_t& operations) const
{
@@ -566,7 +559,7 @@ Matcher<Parser>::ExecuteResult Matcher<Parser>::execute(MatchInput const& input,
}
BumpAllocatedLinkedList<MatchState> states_to_try_next;
HashTable<u64, SufficientlyUniformValueTraits> seen_state_hashes;
HashTable<u64, IdentityHashTraits<u64>> seen_state_hashes;
#if REGEX_DEBUG
size_t recursion_level = 0;
#endif

View File

@@ -23,7 +23,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
}
// Read files, compute their hashes, ignore collisions for now.
HashMap<u32, Vector<ByteString>> inverse_hashes;
HashMap<u32, Vector<ByteString>, IdentityHashTraits<u32>> inverse_hashes;
bool had_errors = false;
for (auto filename : arguments.strings.slice(1)) {