/*
 * Copyright (C) 2021-2024 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright 2018 The Rust Project Developers.
 *
 * Permission is hereby granted, free of charge, to any
 * person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the
 * Software without restriction, including without
 * limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software
 * is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice
 * shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
 * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include <wtf/HashTable.h>
#include <wtf/text/StringHash.h>

namespace WTF {

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
class RobinHoodHashTable;

// 95% load factor. This a bit regress "insertion" performance, while it keeps lookup performance sane.
// RobinHoodHashTable can work with 95% load factor because of maintained probe distance.
struct MemoryCompactLookupOnlyRobinHoodHashTableSizePolicy {
    static constexpr unsigned maxLoadNumerator = 19;
    static constexpr unsigned maxLoadDenominator = 20;
    static constexpr unsigned minLoad = 6;
};

// 90% load factor. RobinHoodHashTable can work with such a high load-factor.
// Observed performance is slightly worse than HashTable (75% for small table, 50% for large table).
struct MemoryCompactRobinHoodHashTableSizePolicy {
    static constexpr unsigned maxLoadNumerator = 9;
    static constexpr unsigned maxLoadDenominator = 10;
    static constexpr unsigned minLoad = 6;
};

// 75% load factor, this maintains the performance same to HashTable, still the load factor
// is higher (HashTable uses 75% for small table, 50 for large table).
struct FastRobinHoodHashTableSizePolicy {
    static constexpr unsigned maxLoadNumerator = 3;
    static constexpr unsigned maxLoadDenominator = 4;
    static constexpr unsigned minLoad = 6;
};

// RobinHood HashTable has several limitations compared to usual HashTable, that's why this is not a default one.
// RobinHood HashTable computes hash much more frequently. This means that the Key should cache computed hash.
// But our default HashTable does not cache hash value because of memory usage. This design means that Key type
// should have hash value intrusively (e.g. WTF::String holds hash value internally).
//
// If the above requirements meet your use case, then you can try RobinHood HashTable.
// The largest benefit is that we can use significantly high load-factor (90% - 95%)!
//
// The algorithm is RobinHood-Hashing + backward shift deletion, described in [1,2].
//
// Naive RobinHoodHashTable has some cases which cause O(N^2) when iterating it and inserting it to some new RobinHoodHashTable
// without reserving capacity and this is because of high load-factor and exposed hash-ordering at iteration[3]. To mitigate it,
// we calculate hash for each table, and do XOR with the hash value to make hash-ordering different for each table.
//
// [1]: https://codecapsule.com/2013/11/11/robin-hood-hashing/
// [2]: https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
// [3]: https://accidentallyquadratic.tumblr.com/post/153545455987/rust-hash-iteration-reinsertion
template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
class RobinHoodHashTable {
public:
    using HashTableType = RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>;
    using iterator = HashTableIterator<HashTableType, Key, Value, Extractor, HashFunctions, Traits, KeyTraits>;
    using const_iterator = HashTableConstIterator<HashTableType, Key, Value, Extractor, HashFunctions, Traits, KeyTraits>;
    using ValueTraits = Traits;
    using KeyType = Key;
    using ValueType = Value;
    using IdentityTranslatorType = IdentityHashTranslator<ValueTraits, HashFunctions>;
    using AddResult = HashTableAddResult<iterator>;

    static constexpr unsigned probeDistanceThreshold = 128;

    static_assert(!KeyTraits::hasIsReleasedWeakValueFunction);
    static_assert(HashFunctions::hasHashInValue);

    RobinHoodHashTable() = default;

    ~RobinHoodHashTable()
    {
        invalidateIterators(this);
        if (m_table)
            deallocateTable(m_table, tableSize());
    }

    RobinHoodHashTable(const RobinHoodHashTable&);
    void swap(RobinHoodHashTable&);
    RobinHoodHashTable& operator=(const RobinHoodHashTable&);

    RobinHoodHashTable(RobinHoodHashTable&&);
    RobinHoodHashTable& operator=(RobinHoodHashTable&&);

    // When the hash table is empty, just return the same iterator for end as for begin.
    // This is more efficient because we don't have to skip all the empty and deleted
    // buckets, and iterating an empty table is a common case that's worth optimizing.
    iterator begin() { return isEmpty() ? end() : makeIterator(m_table); }
    iterator end() { return makeKnownGoodIterator(m_table + tableSize()); }
    const_iterator begin() const { return isEmpty() ? end() : makeConstIterator(m_table); }
    const_iterator end() const { return makeKnownGoodConstIterator(m_table + tableSize()); }

    iterator random()
    {
        if (isEmpty())
            return end();

        while (true) {
            auto& bucket = m_table[weakRandomNumber<uint32_t>() & tableSizeMask()];
            if (!isEmptyBucket(bucket))
                return makeKnownGoodIterator(&bucket);
        }
    }

    const_iterator random() const { return static_cast<const_iterator>(const_cast<RobinHoodHashTable*>(this)->random()); }

    unsigned size() const { return keyCount(); }
    unsigned capacity() const { return tableSize(); }
    bool isEmpty() const { return !keyCount(); }

    void reserveInitialCapacity(unsigned keyCount)
    {
        ASSERT(!m_table);
        ASSERT(!tableSize());

        unsigned minimumTableSize = KeyTraits::minimumTableSize;
        unsigned newTableSize = std::max(minimumTableSize, computeBestTableSize(keyCount));

        m_table = allocateTable(newTableSize);
        m_tableSize = newTableSize;
        m_keyCount = 0;
        m_tableHash = computeTableHash(m_table);
        m_willExpand = false;
        internalCheckTableConsistency();
    }

    AddResult add(const ValueType& value) { return add<IdentityTranslatorType>(Extractor::extract(value), [&]() ALWAYS_INLINE_LAMBDA { return value; }); }
    AddResult add(ValueType&& value) { return add<IdentityTranslatorType>(Extractor::extract(value), [&]() ALWAYS_INLINE_LAMBDA { return WTFMove(value); }); }

    // A special version of add() that finds the object by hashing and comparing
    // with some other type, to avoid the cost of type conversion if the object is already
    // in the table.
    template<typename HashTranslator> AddResult add(auto&& key, const std::invocable<> auto& functor);
    template<typename HashTranslator> AddResult addPassingHashCode(auto&& key, const std::invocable<> auto& functor);

    iterator find(const KeyType& key) { return find<IdentityTranslatorType>(key); }
    const_iterator find(const KeyType& key) const { return find<IdentityTranslatorType>(key); }
    bool contains(const KeyType& key) const { return contains<IdentityTranslatorType>(key); }

    template<typename HashTranslator, typename T> iterator find(const T&);
    template<typename HashTranslator, typename T> const_iterator find(const T&) const;
    template<typename HashTranslator, typename T> bool contains(const T&) const;

    void remove(const KeyType&);
    void remove(iterator);
    void removeWithoutEntryConsistencyCheck(iterator);
    void removeWithoutEntryConsistencyCheck(const_iterator);
    void clear();

    static bool isEmptyBucket(const ValueType& value) { return isHashTraitsEmptyValue<KeyTraits>(Extractor::extract(value)); }
    static bool isEmptyOrDeletedBucket(const ValueType& value) { return isEmptyBucket(value); }

    ValueType* lookup(const Key& key) { return lookup<IdentityTranslatorType>(key); }
    template<typename HashTranslator, typename T> ValueType* lookup(const T&);
    template<typename HashTranslator, typename T> ValueType* inlineLookup(const T&);

#if ASSERT_ENABLED
        void checkTableConsistency() const;
#else
        static void checkTableConsistency() { }
#endif

#if CHECK_HASHTABLE_CONSISTENCY
        void internalCheckTableConsistency() const { checkTableConsistency(); }
        void internalCheckTableConsistencyExceptSize() const { checkTableConsistencyExceptSize(); }
#else
        static void internalCheckTableConsistencyExceptSize() { }
        static void internalCheckTableConsistency() { }
#endif

    static constexpr bool shouldExpand(uint64_t keyCount, uint64_t tableSize)
    {
        return keyCount * maxLoadDenominator >= tableSize * maxLoadNumerator;
    }

private:
    static ValueType* allocateTable(unsigned size);
    static void deallocateTable(ValueType* table, unsigned size);

    using LookupType = std::pair<ValueType*, bool>;

    template<typename HashTranslator, typename T> void checkKey(const T&);

    void maintainProbeDistanceForAdd(ValueType&&, unsigned index, unsigned distance, unsigned size, unsigned sizeMask, unsigned tableHash);

    void removeAndInvalidateWithoutEntryConsistencyCheck(ValueType*);
    void removeAndInvalidate(ValueType*);
    void remove(ValueType*);

    static unsigned computeTableHash(ValueType* table) { return DefaultHash<ValueType*>::hash(table); }

    static constexpr unsigned computeBestTableSize(unsigned keyCount);
    bool shouldExpand() const
    {
        unsigned keyCount = this->keyCount();
        unsigned tableSize = this->tableSize();
        if (shouldExpand(keyCount, tableSize))
            return true;
        // If probe-length exceeds probeDistanceThreshold, and 50%~ is filled, expand the table.
        return m_willExpand && keyCount * 2 >= tableSize;
    }
    bool shouldShrink() const { return keyCount() * minLoad < tableSize() && tableSize() > KeyTraits::minimumTableSize; }
    void expand();
    void shrink() { rehash(tableSize() / 2); }
    void shrinkToBestSize();

    void rehash(unsigned newTableSize);
    void reinsert(ValueType&&);

    static void initializeBucket(ValueType& bucket);
    static void deleteBucket(ValueType& bucket) { hashTraitsDeleteBucket<Traits>(bucket); }

    static constexpr unsigned desiredIndex(unsigned hash, unsigned sizeMask)
    {
        return hash & sizeMask;
    }

    static constexpr unsigned probeDistance(unsigned hash, unsigned index, unsigned size, unsigned sizeMask)
    {
        return (index + size - desiredIndex(hash, sizeMask)) & sizeMask;
    }

    iterator makeIterator(ValueType* pos) { return iterator(this, pos, m_table + tableSize()); }
    const_iterator makeConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + tableSize()); }
    iterator makeKnownGoodIterator(ValueType* pos) { return iterator(this, pos, m_table + tableSize(), HashItemKnownGood); }
    const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + tableSize(), HashItemKnownGood); }

#if ASSERT_ENABLED
        void checkTableConsistencyExceptSize() const;
#else
        static void checkTableConsistencyExceptSize() { }
#endif

    static constexpr unsigned maxLoadNumerator = SizePolicy::maxLoadNumerator;
    static constexpr unsigned maxLoadDenominator = SizePolicy::maxLoadDenominator;
    static constexpr unsigned minLoad = SizePolicy::minLoad;

    unsigned tableSize() const { return m_tableSize; }
    unsigned tableSizeMask() const { return m_tableSize - 1; }
    unsigned keyCount() const { return m_keyCount; }
    unsigned tableHash() const { return m_tableHash; }

    ValueType* m_table { nullptr };
    unsigned m_tableSize { 0 };
    unsigned m_keyCount { 0 };
    unsigned m_tableHash { 0 };
    bool m_willExpand { false };

#if CHECK_HASHTABLE_ITERATORS
public:
    // All access to m_iterators should be guarded with m_mutex.
    mutable const_iterator* m_iterators { nullptr };
    // Use std::unique_ptr so HashTable can still be memmove'd or memcpy'ed.
    mutable std::unique_ptr<Lock> m_mutex { makeUnique<Lock>() };
#endif
};

#if !ASSERT_ENABLED

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template<typename HashTranslator, typename T>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::checkKey(const T&)
{
}

#else // ASSERT_ENABLED

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template<typename HashTranslator, typename T>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::checkKey(const T& key)
{
    if (!HashFunctions::safeToCompareToEmptyOrDeleted)
        return;
    ASSERT(!HashTranslator::equal(KeyTraits::emptyValue(), key));
    ALLOW_DEPRECATED_DECLARATIONS_BEGIN
    typename std::aligned_storage<sizeof(ValueType), std::alignment_of<ValueType>::value>::type deletedValueBuffer;
    ALLOW_DEPRECATED_DECLARATIONS_END
    ValueType* deletedValuePtr = reinterpret_cast_ptr<ValueType*>(&deletedValueBuffer);
    ValueType& deletedValue = *deletedValuePtr;
    Traits::constructDeletedValue(deletedValue);
    ASSERT(!HashTranslator::equal(Extractor::extract(deletedValue), key));
}

#endif // ASSERT_ENABLED

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template<typename HashTranslator, typename T>
inline auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::lookup(const T& key) -> ValueType*
{
    return inlineLookup<HashTranslator>(key);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template<typename HashTranslator, typename T>
ALWAYS_INLINE auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::inlineLookup(const T& key) -> ValueType*
{
    checkKey<HashTranslator>(key);

    ValueType* table = m_table;
    if (!table)
        return nullptr;

    unsigned size = tableSize();
    unsigned sizeMask = tableSizeMask();
    unsigned tableHash = this->tableHash();
    unsigned hash = HashTranslator::hash(key) ^ tableHash;
    unsigned index = desiredIndex(hash, sizeMask);
    unsigned distance = 0;

    while (true) {
        ValueType* entry = m_table + index;
        if (isEmptyBucket(*entry))
            return nullptr;

        // RobinHoodHashTable maintains this invariant so that we can stop
        // probing when distance exceeds probing distance of the existing entry.
        auto& entryKey = Extractor::extract(*entry);
        unsigned entryHash = IdentityTranslatorType::hash(entryKey) ^ tableHash;
        if (distance > probeDistance(entryHash, index, size, sizeMask))
            return nullptr;

        if (entryHash == hash && HashTranslator::equal(entryKey, key))
            return entry;

        index = (index + 1) & sizeMask;
        ++distance;
    }
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::initializeBucket(ValueType& bucket)
{
    HashTableBucketInitializer<Traits::emptyValueIsZero>::template initialize<Traits>(bucket);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template<typename HashTranslator, typename T>
ALWAYS_INLINE auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::add(T&& key, const std::invocable<> auto& functor) -> AddResult
{
    checkKey<HashTranslator>(key);

    invalidateIterators(this);

    // We should expand before potentially inserting an entry. If we expand a table after inserting an entry,
    // we need to re-look up entry from the table since the inserted entry position is not stable during rehasing.
    if (shouldExpand())
        expand();

    internalCheckTableConsistency();

    ASSERT(m_table);

    unsigned size = tableSize();
    unsigned sizeMask = tableSizeMask();
    unsigned tableHash = this->tableHash();
    unsigned hash = HashTranslator::hash(key) ^ tableHash;
    unsigned index = desiredIndex(hash, sizeMask);
    unsigned distance = 0;

    ValueType* entry = nullptr;
    while (true) {
        entry = m_table + index;
        if (isEmptyBucket(*entry)) {
            if (distance >= probeDistanceThreshold)
                m_willExpand = true;
            HashTranslator::translate(*entry, std::forward<T>(key), functor);
            break;
        }

        auto& entryKey = Extractor::extract(*entry);
        unsigned entryHash = IdentityTranslatorType::hash(entryKey) ^ tableHash;
        unsigned entryDistance = probeDistance(entryHash, index, size, sizeMask);
        if (distance > entryDistance) {
            if (distance >= probeDistanceThreshold)
                m_willExpand = true;
            // Start swapping existing entry to maintain probe-distance invariant.
            ValueType existingEntry = WTFMove(*entry);
            entry->~ValueType();
            initializeBucket(*entry);
            HashTranslator::translate(*entry, std::forward<T>(key), functor);
            maintainProbeDistanceForAdd(WTFMove(existingEntry), index, entryDistance, size, sizeMask, tableHash);
            break;
        }

        if (entryHash == hash && HashTranslator::equal(entryKey, key))
            return AddResult(makeKnownGoodIterator(entry), false);

        index = (index + 1) & sizeMask;
        ++distance;
    }

    m_keyCount += 1;

    internalCheckTableConsistency();

    return AddResult(makeKnownGoodIterator(entry), true);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
ALWAYS_INLINE void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::maintainProbeDistanceForAdd(ValueType&& value, unsigned index, unsigned distance, unsigned size, unsigned sizeMask, unsigned tableHash)
{
    using std::swap; // For C++ ADL.
    index = (index + 1) & sizeMask;
    ++distance;

    while (true) {
        ValueType* entry = m_table + index;
        if (isEmptyBucket(*entry)) {
            ValueTraits::assignToEmpty(*entry, WTFMove(value));
            return;
        }

        unsigned entryHash = IdentityTranslatorType::hash(Extractor::extract(*entry)) ^ tableHash;
        unsigned entryDistance = probeDistance(entryHash, index, size, sizeMask);
        if (distance > entryDistance) {
            swap(value, *entry);
            distance = entryDistance;
        }

        index = (index + 1) & sizeMask;
        ++distance;
    }
}


template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template<typename HashTranslator, typename T>
inline auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::addPassingHashCode(T&& key, const std::invocable<> auto& functor) -> AddResult
{
    checkKey<HashTranslator>(key);

    invalidateIterators(this);

    // We should expand before potentially inserting an entry. If we expand a table after inserting an entry,
    // we need to re-look up entry from the table since the inserted entry position is not stable during rehasing.
    if (shouldExpand())
        expand();

    internalCheckTableConsistency();

    ASSERT(m_table);

    unsigned size = tableSize();
    unsigned sizeMask = tableSizeMask();
    unsigned tableHash = this->tableHash();
    unsigned originalHash = HashTranslator::hash(key);
    unsigned hash = originalHash ^ tableHash;
    unsigned index = desiredIndex(hash, sizeMask);
    unsigned distance = 0;

    ValueType* entry = nullptr;
    while (true) {
        entry = m_table + index;
        if (isEmptyBucket(*entry)) {
            if (distance >= probeDistanceThreshold)
                m_willExpand = true;
            HashTranslator::translate(*entry, std::forward<T>(key), functor, originalHash);
            break;
        }

        auto& entryKey = Extractor::extract(*entry);
        unsigned entryHash = IdentityTranslatorType::hash(entryKey) ^ tableHash;
        unsigned entryDistance = probeDistance(entryHash, index, size, sizeMask);
        if (distance > entryDistance) {
            if (distance >= probeDistanceThreshold)
                m_willExpand = true;
            // Start swapping existing entry to maintain probe-distance invariant.
            ValueType existingEntry = WTFMove(*entry);
            entry->~ValueType();
            initializeBucket(*entry);
            HashTranslator::translate(*entry, std::forward<T>(key), functor, originalHash);
            maintainProbeDistanceForAdd(WTFMove(existingEntry), index, entryDistance, size, sizeMask, tableHash);
            break;
        }

        if (entryHash == hash && HashTranslator::equal(entryKey, key))
            return AddResult(makeKnownGoodIterator(entry), false);

        index = (index + 1) & sizeMask;
        ++distance;
    }

    m_keyCount += 1;

    internalCheckTableConsistency();

    return AddResult(makeKnownGoodIterator(entry), true);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::reinsert(ValueType&& value)
{
    using std::swap; // For C++ ADL.
    unsigned size = tableSize();
    unsigned sizeMask = tableSizeMask();
    unsigned tableHash = this->tableHash();
    unsigned hash = IdentityTranslatorType::hash(Extractor::extract(value)) ^ tableHash;
    unsigned index = desiredIndex(hash, sizeMask);
    unsigned distance = 0;

    while (true) {
        ValueType* entry = m_table + index;
        if (isEmptyBucket(*entry)) {
            ValueTraits::assignToEmpty(*entry, WTFMove(value));
            return;
        }

        unsigned entryHash = IdentityTranslatorType::hash(Extractor::extract(*entry)) ^ tableHash;
        unsigned entryDistance = probeDistance(entryHash, index, size, sizeMask);
        if (distance > entryDistance) {
            swap(value, *entry);
            distance = entryDistance;
        }

        index = (index + 1) & sizeMask;
        ++distance;
    }
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template <typename HashTranslator, typename T>
auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::find(const T& key) -> iterator
{
    if (!m_table)
        return end();

    ValueType* entry = lookup<HashTranslator>(key);
    if (!entry)
        return end();

    return makeKnownGoodIterator(entry);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template <typename HashTranslator, typename T>
auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::find(const T& key) const -> const_iterator
{
    if (!m_table)
        return end();

    ValueType* entry = const_cast<RobinHoodHashTable*>(this)->lookup<HashTranslator>(key);
    if (!entry)
        return end();

    return makeKnownGoodConstIterator(entry);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
template <typename HashTranslator, typename T>
bool RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::contains(const T& key) const
{
    if (!m_table)
        return false;

    return const_cast<RobinHoodHashTable*>(this)->lookup<HashTranslator>(key);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::removeAndInvalidateWithoutEntryConsistencyCheck(ValueType* pos)
{
    invalidateIterators(this);
    remove(pos);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::removeAndInvalidate(ValueType* pos)
{
    invalidateIterators(this);
    internalCheckTableConsistency();
    remove(pos);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::remove(ValueType* pos)
{
    // This is removal via "backward-shift-deletion". This basically shift existing entries to removed empty entry place so that we make
    // the table as if no removal happened so far. This decreases distance-to-initial-bucket (DIB) of the subsequent entries by 1. This maintains
    // DIB of the table low and relatively constant even if we have many removals, compared to using tombstones.
    // https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
    deleteBucket(*pos);
    initializeBucket(*pos);
    m_keyCount -= 1;

    unsigned size = tableSize();
    unsigned sizeMask = tableSizeMask();
    unsigned tableHash = this->tableHash();
    unsigned indexPrevious = pos - m_table;
    unsigned index = (indexPrevious + 1) & sizeMask;

    while (true) {
        Value* previousEntry = m_table + indexPrevious;
        Value* entry = m_table + index;
        if (isEmptyBucket(*entry))
            break;

        ASSERT(isEmptyBucket(*previousEntry));
        auto& entryKey = Extractor::extract(*entry);
        unsigned entryHash = IdentityTranslatorType::hash(entryKey) ^ tableHash;
        if (!probeDistance(entryHash, index, size, sizeMask))
            break;

        ValueTraits::assignToEmpty(*previousEntry, WTFMove(*entry));
        entry->~ValueType();
        initializeBucket(*entry);

        indexPrevious = index;
        index = (index + 1) & sizeMask;
    }

    if (shouldShrink())
        shrink();

    internalCheckTableConsistency();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::remove(iterator it)
{
    if (it == end())
        return;

    removeAndInvalidate(const_cast<ValueType*>(it.m_iterator.m_position));
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::removeWithoutEntryConsistencyCheck(iterator it)
{
    if (it == end())
        return;

    removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType*>(it.m_iterator.m_position));
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::removeWithoutEntryConsistencyCheck(const_iterator it)
{
    if (it == end())
        return;

    removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType*>(it.m_position));
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::remove(const KeyType& key)
{
    remove(find(key));
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::allocateTable(unsigned size) -> ValueType*
{
    // would use a template member function with explicit specializations here, but
    // gcc doesn't appear to support that
    if constexpr (Traits::emptyValueIsZero)
        return reinterpret_cast_ptr<ValueType*>(static_cast<char*>(HashTableMalloc::zeroedMalloc(size * sizeof(ValueType))));

    ValueType* result = reinterpret_cast_ptr<ValueType*>(static_cast<char*>(HashTableMalloc::malloc(size * sizeof(ValueType))));
    for (unsigned i = 0; i < size; i++)
        initializeBucket(result[i]);
    return result;
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::deallocateTable(ValueType* table, unsigned size)
{
    for (unsigned i = 0; i < size; ++i)
        table[i].~ValueType();
    HashTableMalloc::free(reinterpret_cast<char*>(table));
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::expand()
{
    unsigned newSize;
    unsigned oldSize = tableSize();
    if (!oldSize)
        newSize = KeyTraits::minimumTableSize;
    else
        newSize = oldSize * 2;

    rehash(newSize);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
constexpr unsigned RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::computeBestTableSize(unsigned keyCount)
{
    unsigned bestTableSize = WTF::roundUpToPowerOfTwo(keyCount);

    if (shouldExpand(keyCount, bestTableSize))
        bestTableSize *= 2;

    auto aboveThresholdForEagerExpansion = [](double loadFactor, unsigned keyCount, unsigned tableSize)
    {
        // Here is the rationale behind this calculation, using 3/4 load-factor.
        // With maxLoad at 3/4 and minLoad at 1/6, our average load is 11/24.
        // If we are getting half-way between 11/24 and 3/4, we double the size
        // to avoid being too close to loadMax and bring the ratio close to 11/24. This
        // give us a load in the bounds [9/24, 15/24).
        double maxLoadRatio = loadFactor;
        double minLoadRatio = 1.0 / minLoad;
        double averageLoadRatio = (maxLoadRatio + minLoadRatio) / 2;
        double halfWayBetweenAverageAndMaxLoadRatio = (averageLoadRatio + maxLoadRatio) / 2;
        return keyCount >= tableSize * halfWayBetweenAverageAndMaxLoadRatio;
    };

    constexpr double loadFactor = static_cast<double>(maxLoadNumerator) / maxLoadDenominator;
    if (aboveThresholdForEagerExpansion(loadFactor, keyCount, bestTableSize))
        bestTableSize *= 2;
    unsigned minimumTableSize = KeyTraits::minimumTableSize;
    return std::max(bestTableSize, minimumTableSize);
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::shrinkToBestSize()
{
    unsigned minimumTableSize = KeyTraits::minimumTableSize;
    rehash(std::max(minimumTableSize, computeBestTableSize(keyCount())));
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::rehash(unsigned newTableSize)
{
    internalCheckTableConsistencyExceptSize();

    unsigned oldTableSize = tableSize();
    ValueType* oldTable = m_table;

    m_table = allocateTable(newTableSize);
    m_tableSize = newTableSize;
    m_tableHash = computeTableHash(m_table);
    m_willExpand = false;

    for (unsigned i = 0; i < oldTableSize; ++i) {
        auto* oldEntry = oldTable + i;
        if (!isEmptyBucket(*oldEntry))
            reinsert(WTFMove(*oldEntry));
        oldEntry->~ValueType();
    }

    if (oldTable)
        HashTableMalloc::free(reinterpret_cast<char*>(oldTable));

    internalCheckTableConsistency();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::clear()
{
    invalidateIterators(this);
    if (!m_table)
        return;

    unsigned oldTableSize = tableSize();
    m_tableSize = 0;
    m_keyCount = 0;
    m_tableHash = 0;
    m_willExpand = false;
    deallocateTable(std::exchange(m_table, nullptr), oldTableSize);
    internalCheckTableConsistency();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::RobinHoodHashTable(const RobinHoodHashTable& other)
{
    if (!other.m_tableSize || !other.m_keyCount)
        return;

    m_table = allocateTable(other.m_tableSize);
    m_tableSize = other.m_tableSize;
    m_keyCount = other.m_keyCount;
    m_tableHash = computeTableHash(m_table);
    m_willExpand = other.m_willExpand;

    for (unsigned index = 0; index < other.m_tableSize; ++index) {
        ValueType& otherEntry = other.m_table[index];
        if (!isEmptyBucket(otherEntry)) {
            ValueType entry(otherEntry);
            reinsert(WTFMove(entry));
        }
    }
    internalCheckTableConsistency();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::swap(RobinHoodHashTable& other)
{
    using std::swap; // For C++ ADL.
    invalidateIterators(this);
    invalidateIterators(&other);

    swap(m_table, other.m_table);
    swap(m_tableSize, other.m_tableSize);
    swap(m_keyCount, other.m_keyCount);
    swap(m_tableHash, other.m_tableHash);
    swap(m_willExpand, other.m_willExpand);

    internalCheckTableConsistency();
    other.internalCheckTableConsistency();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::operator=(const RobinHoodHashTable& other) -> RobinHoodHashTable&
{
    RobinHoodHashTable tmp(other);
    swap(tmp);
    return *this;
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::RobinHoodHashTable(RobinHoodHashTable&& other)
{
    invalidateIterators(&other);

    m_table = std::exchange(other.m_table, nullptr);
    m_tableSize = std::exchange(other.m_tableSize, 0);
    m_keyCount = std::exchange(other.m_keyCount, 0);
    m_tableHash = std::exchange(other.m_tableHash, 0);
    m_willExpand = std::exchange(other.m_willExpand, false);

    internalCheckTableConsistency();
    other.internalCheckTableConsistency();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
inline auto RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::operator=(RobinHoodHashTable&& other) -> RobinHoodHashTable&
{
    RobinHoodHashTable temp(WTFMove(other));
    swap(temp);
    return *this;
}


#if ASSERT_ENABLED

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::checkTableConsistency() const
{
    checkTableConsistencyExceptSize();
}

template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename SizePolicy>
void RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, SizePolicy>::checkTableConsistencyExceptSize() const
{
    if (!m_table)
        return;

    unsigned count = 0;
    unsigned tableSize = this->tableSize();
    for (unsigned i = 0; i < tableSize; ++i) {
        ValueType* entry = m_table + i;
        if (isEmptyBucket(*entry))
            continue;

        auto& key = Extractor::extract(*entry);
        const_iterator it = find(key);
        ASSERT(entry == it.m_position);
        ++count;

        ValueCheck<Key>::checkConsistency(key);
    }

    ASSERT(count == keyCount());
    ASSERT(this->tableSize() >= KeyTraits::minimumTableSize);
    ASSERT(tableSizeMask());
    ASSERT(this->tableSize() == tableSizeMask() + 1);
}

#endif // ASSERT_ENABLED

struct MemoryCompactLookupOnlyRobinHoodHashTableTraits {
    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
    using TableType = RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, MemoryCompactLookupOnlyRobinHoodHashTableSizePolicy>;
};

struct MemoryCompactRobinHoodHashTableTraits {
    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
    using TableType = RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, MemoryCompactRobinHoodHashTableSizePolicy>;
};

struct FastRobinHoodHashTableTraits {
    template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits>
    using TableType = RobinHoodHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, FastRobinHoodHashTableSizePolicy>;
};

} // namespace WTF
