Skip to content

Commit

Permalink
HashMap: Allow hashing of differently typed keys
Browse files Browse the repository at this point in the history
  • Loading branch information
rune-scape committed Sep 19, 2024
1 parent a93f9fe commit 3a74a8d
Showing 1 changed file with 90 additions and 42 deletions.
132 changes: 90 additions & 42 deletions core/templates/hash_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,19 @@ class HashMap {
uint32_t capacity_index = 0;
uint32_t num_elements = 0;

_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
uint32_t hash = Hasher::hash(p_key);
_FORCE_INLINE_ uint32_t _hash(TKey &&p_key) const {
uint32_t hash = Hasher::hash(static_cast<TKey>(p_key));

if (unlikely(hash == EMPTY_HASH)) {
hash = EMPTY_HASH + 1;
}

return hash;
}

template <typename TKeyParam>
_FORCE_INLINE_ uint32_t _hash(TKeyParam &&p_key) const {
uint32_t hash = Hasher::hash(static_cast<TKeyParam>(p_key));

Check failure on line 96 in core/templates/hash_map.h

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'HashMapHasherDefault::hash': ambiguous call to overloaded function

Check failure on line 96 in core/templates/hash_map.h

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'HashMapHasherDefault::hash': ambiguous call to overloaded function

Check failure on line 96 in core/templates/hash_map.h

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release)

'HashMapHasherDefault::hash': ambiguous call to overloaded function

Check failure on line 96 in core/templates/hash_map.h

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release)

'HashMapHasherDefault::hash': ambiguous call to overloaded function

if (unlikely(hash == EMPTY_HASH)) {
hash = EMPTY_HASH + 1;
Expand All @@ -96,14 +107,15 @@ class HashMap {
return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity);
}

bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
template <typename TKeyParam>
bool _lookup_pos(TKeyParam &&p_key, uint32_t &r_pos) const {
if (elements == nullptr || num_elements == 0) {
return false; // Failed lookups, no elements
}

const uint32_t capacity = hash_table_size_primes[capacity_index];
const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index];
uint32_t hash = _hash(p_key);
uint32_t hash = _hash(static_cast<TKeyParam>(p_key));
uint32_t pos = fastmod(hash, capacity_inv, capacity);
uint32_t distance = 0;

Expand All @@ -116,7 +128,7 @@ class HashMap {
return false;
}

if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, p_key)) {
if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, static_cast<TKeyParam>(p_key))) {
r_pos = pos;
return true;
}
Expand Down Expand Up @@ -194,7 +206,8 @@ class HashMap {
Memory::free_static(old_hashes);
}

_FORCE_INLINE_ HashMapElement<TKey, TValue> *_insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) {
template <typename TKeyParam, typename TValueParam>
_FORCE_INLINE_ HashMapElement<TKey, TValue> *_insert(TKeyParam &&p_key, TValueParam &&p_value, bool p_front_insert = false) {
uint32_t capacity = hash_table_size_primes[capacity_index];
if (unlikely(elements == nullptr)) {
// Allocate on demand to save memory.
Expand All @@ -209,18 +222,18 @@ class HashMap {
}

uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);

if (exists) {
elements[pos]->data.value = p_value;
elements[pos]->data.value = static_cast<TValueParam>(p_value);
return elements[pos];
} else {
if (num_elements + 1 > MAX_OCCUPANCY * capacity) {
ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, nullptr, "Hash table maximum capacity reached, aborting insertion.");
_resize_and_rehash(capacity_index + 1);
}

HashMapElement<TKey, TValue> *elem = element_alloc.new_allocation(HashMapElement<TKey, TValue>(p_key, p_value));
HashMapElement<TKey, TValue> *elem = element_alloc.new_allocation(HashMapElement<TKey, TValue>(static_cast<TKeyParam>(p_key), static_cast<TValueParam>(p_value)));

if (tail_element == nullptr) {
head_element = elem;
Expand All @@ -235,7 +248,7 @@ class HashMap {
tail_element = elem;
}

uint32_t hash = _hash(p_key);
uint32_t hash = _hash(static_cast<TKeyParam>(p_key));
_insert_with_hash(hash, elem);
return elem;
}
Expand Down Expand Up @@ -271,48 +284,54 @@ class HashMap {
num_elements = 0;
}

TValue &get(const TKey &p_key) {
template <typename TKeyParam>
TValue &get(TKeyParam &&p_key) {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);
CRASH_COND_MSG(!exists, "HashMap key not found.");
return elements[pos]->data.value;
}

const TValue &get(const TKey &p_key) const {
template <typename TKeyParam>
const TValue &get(TKeyParam &&p_key) const {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);
CRASH_COND_MSG(!exists, "HashMap key not found.");
return elements[pos]->data.value;
}

const TValue *getptr(const TKey &p_key) const {
template <typename TKeyParam>
const TValue *getptr(TKeyParam &&p_key) const {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);

if (exists) {
return &elements[pos]->data.value;
}
return nullptr;
}

TValue *getptr(const TKey &p_key) {
template <typename TKeyParam>
TValue *getptr(TKeyParam &&p_key) {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);

if (exists) {
return &elements[pos]->data.value;
}
return nullptr;
}

_FORCE_INLINE_ bool has(const TKey &p_key) const {
template <typename TKeyParam>
_FORCE_INLINE_ bool has(TKeyParam &&p_key) const {
uint32_t _pos = 0;
return _lookup_pos(p_key, _pos);
return _lookup_pos(static_cast<TKeyParam>(p_key), _pos);
}

bool erase(const TKey &p_key) {
template <typename TKeyParam>
bool erase(TKeyParam &&p_key) {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);

if (!exists) {
return false;
Expand Down Expand Up @@ -355,13 +374,14 @@ class HashMap {

// Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration.
// p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key.
bool replace_key(const TKey &p_old_key, const TKey &p_new_key) {
if (p_old_key == p_new_key) {
template <typename TKeyParamOld, typename TKeyParamNew>
bool replace_key(TKeyParamOld &&p_old_key, TKeyParamNew &&p_new_key) {
if (static_cast<TKeyParamOld>(p_old_key) == static_cast<TKeyParamNew>(p_new_key)) {
return true;
}
uint32_t pos = 0;
ERR_FAIL_COND_V(_lookup_pos(p_new_key, pos), false);
ERR_FAIL_COND_V(!_lookup_pos(p_old_key, pos), false);
ERR_FAIL_COND_V(_lookup_pos(static_cast<TKeyParamNew>(p_new_key), pos), false);
ERR_FAIL_COND_V(!_lookup_pos(static_cast<TKeyParamOld>(p_old_key), pos), false);
HashMapElement<TKey, TValue> *element = elements[pos];

// Delete the old entries in hashes and elements.
Expand All @@ -380,8 +400,8 @@ class HashMap {
num_elements--;

// Update the HashMapElement with the new key and reinsert it.
const_cast<TKey &>(element->data.key) = p_new_key;
uint32_t hash = _hash(p_new_key);
const_cast<TKey &>(element->data.key) = static_cast<TKeyParamNew>(p_new_key);
uint32_t hash = _hash(static_cast<TKeyParamNew>(p_new_key));
_insert_with_hash(hash, element);

return true;
Expand Down Expand Up @@ -496,9 +516,10 @@ class HashMap {
return Iterator(tail_element);
}

_FORCE_INLINE_ Iterator find(const TKey &p_key) {
template <typename TKeyParam>
_FORCE_INLINE_ Iterator find(TKeyParam &&p_key) {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);
if (!exists) {
return end();
}
Expand All @@ -521,9 +542,10 @@ class HashMap {
return ConstIterator(tail_element);
}

_FORCE_INLINE_ ConstIterator find(const TKey &p_key) const {
template <typename TKeyParam>
_FORCE_INLINE_ ConstIterator find(TKeyParam &&p_key) const {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);
if (!exists) {
return end();
}
Expand All @@ -532,27 +554,30 @@ class HashMap {

/* Indexing */

const TValue &operator[](const TKey &p_key) const {
template <typename TKeyParam>
const TValue &operator[](TKeyParam &&p_key) const {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);
CRASH_COND(!exists);
return elements[pos]->data.value;
}

TValue &operator[](const TKey &p_key) {
template <typename TKeyParam>
TValue &operator[](TKeyParam &&p_key) {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
bool exists = _lookup_pos(static_cast<TKeyParam>(p_key), pos);
if (!exists) {
return _insert(p_key, TValue())->data.value;
return _insert(static_cast<TKeyParam>(p_key), TValue())->data.value;
} else {
return elements[pos]->data.value;
}
}

/* Insert */

Iterator insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) {
return Iterator(_insert(p_key, p_value, p_front_insert));
template <typename TKeyParam>
Iterator insert(TKeyParam &&p_key, TValue p_value, bool p_front_insert = false) {
return Iterator(_insert(static_cast<TKeyParam>(p_key), static_cast<TValue &&>(p_value), p_front_insert));
}

/* Constructors */
Expand All @@ -569,9 +594,19 @@ class HashMap {
}
}

void operator=(const HashMap &p_other) {
HashMap(HashMap &&p_other) {
SWAP(element_alloc, p_other.element_alloc);
SWAP(elements, p_other.elements);
SWAP(hashes, p_other.hashes);
SWAP(head_element, p_other.head_element);
SWAP(tail_element, p_other.tail_element);
SWAP(capacity_index, p_other.capacity_index);
SWAP(num_elements, p_other.num_elements);
}

HashMap &operator=(const HashMap &p_other) {
if (this == &p_other) {
return; // Ignore self assignment.
return *this; // Ignore self assignment.
}
if (num_elements != 0) {
clear();
Expand All @@ -580,12 +615,25 @@ class HashMap {
reserve(hash_table_size_primes[p_other.capacity_index]);

if (p_other.elements == nullptr) {
return; // Nothing to copy.
return *this; // Nothing to copy.
}

for (const KeyValue<TKey, TValue> &E : p_other) {
insert(E.key, E.value);
}

return *this;
}

HashMap &operator=(HashMap &&p_other) {
SWAP(element_alloc, p_other.element_alloc);
SWAP(elements, p_other.elements);
SWAP(hashes, p_other.hashes);
SWAP(head_element, p_other.head_element);
SWAP(tail_element, p_other.tail_element);
SWAP(capacity_index, p_other.capacity_index);
SWAP(num_elements, p_other.num_elements);
return *this;
}

HashMap(uint32_t p_initial_capacity) {
Expand Down

0 comments on commit 3a74a8d

Please sign in to comment.