From 75d20331cc97b3c513302fb60567cf8fc9e9d480 Mon Sep 17 00:00:00 2001 From: Togira <70365614+Togira123@users.noreply.github.com> Date: Thu, 30 Jul 2026 23:21:34 +0200 Subject: [PATCH] sync templates --- include/godot_cpp/core/defs.hpp | 23 +- include/godot_cpp/templates/hash_map.hpp | 2 +- include/godot_cpp/templates/hash_set.hpp | 465 ++++++++++-------- include/godot_cpp/templates/hashfuncs.hpp | 347 +++---------- include/godot_cpp/templates/list.hpp | 329 +++++-------- .../templates/{sort_list.h => sort_list.hpp} | 2 +- include/godot_cpp/templates/span.hpp | 12 +- include/godot_cpp/templates/vset.hpp | 61 +-- src/templates/hashfuncs.cpp | 154 ++++++ tools/godotcpp.py | 1 + 10 files changed, 645 insertions(+), 751 deletions(-) rename include/godot_cpp/templates/{sort_list.h => sort_list.hpp} (98%) create mode 100644 src/templates/hashfuncs.cpp diff --git a/include/godot_cpp/core/defs.hpp b/include/godot_cpp/core/defs.hpp index 95ec75ad0..3d40db1c4 100644 --- a/include/godot_cpp/core/defs.hpp +++ b/include/godot_cpp/core/defs.hpp @@ -176,23 +176,23 @@ namespace godot { #undef CLAMP template -constexpr const T SIGN(const T m_v) { - return m_v > 0 ? +1.0f : (m_v < 0 ? -1.0f : 0.0f); +constexpr const T SIGN(const T p_value) { + return p_value > 0 ? +1.0f : (p_value < 0 ? -1.0f : 0.0f); } template -constexpr auto MIN(const T m_a, const T2 m_b) { - return m_a < m_b ? m_a : m_b; +constexpr auto MIN(const T p_left, const T2 p_right) { + return p_left < p_right ? p_left : p_right; } template -constexpr auto MAX(const T m_a, const T2 m_b) { - return m_a > m_b ? m_a : m_b; +constexpr auto MAX(const T p_left, const T2 p_right) { + return p_left > p_right ? p_left : p_right; } template -constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) { - return m_a < m_min ? m_min : (m_a > m_max ? m_max : m_a); +constexpr auto CLAMP(const T p_value, const T2 p_min, const T3 p_max) { + return p_value < p_min ? p_min : (p_value > p_max ? p_max : p_value); } // Like std::size, but without requiring any additional includes. @@ -308,6 +308,13 @@ struct is_zero_constructible : is_zero_constructible {}; template inline constexpr bool is_zero_constructible_v = is_zero_constructible::value; +#if GD_HAS_CPP_ATTRIBUTE(gnu::warn_unused) +// https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#index-warn_005funused +#define _WARN_UNUSED_ [[gnu::warn_unused]] +#else +#define _WARN_UNUSED_ +#endif + // Warning suppression helper macros. #if defined(__clang__) #define GODOT_CLANG_PRAGMA(m_content) _Pragma(#m_content) diff --git a/include/godot_cpp/templates/hash_map.hpp b/include/godot_cpp/templates/hash_map.hpp index b6d6de01c..fe01747ce 100644 --- a/include/godot_cpp/templates/hash_map.hpp +++ b/include/godot_cpp/templates/hash_map.hpp @@ -30,11 +30,11 @@ #pragma once -#include #include #include #include #include +#include #include diff --git a/include/godot_cpp/templates/hash_set.hpp b/include/godot_cpp/templates/hash_set.hpp index a4eb68546..015c8a93e 100644 --- a/include/godot_cpp/templates/hash_set.hpp +++ b/include/godot_cpp/templates/hash_set.hpp @@ -32,38 +32,36 @@ #include #include -#include #include -#include namespace godot { /** - * Implementation of Set using a bidi indexed hash map. - * Use RBSet instead of this only if the following conditions are met: + * Set container using robin hood hashing. * - * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime. - * - Iteration order does matter (via operator<) + * Elements are not pointer stable. + * The element order is arbitrary. * + * Core container guidance: + * https://docs.godotengine.org/en/latest/engine_details/architecture/core_types.html#containers */ - template > -class HashSet { +class _WARN_UNUSED_ HashSet { public: static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. static constexpr float MAX_OCCUPANCY = 0.75; static constexpr uint32_t EMPTY_HASH = 0; private: - TKey *keys = nullptr; - uint32_t *hash_to_key = nullptr; - uint32_t *key_to_hash = nullptr; - uint32_t *hashes = nullptr; + TKey *_keys = nullptr; + uint32_t *_hash_idx_to_key_idx = nullptr; + uint32_t *_key_idx_to_hash_idx = nullptr; + uint32_t *_hashes = nullptr; - uint32_t capacity_index = 0; - uint32_t num_elements = 0; + uint32_t _capacity_idx = 0; + uint32_t _size = 0; _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { uint32_t hash = Hasher::hash(p_key); @@ -75,90 +73,97 @@ class HashSet { return hash; } - static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_pos, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { - const uint32_t original_pos = fastmod(p_hash, p_capacity_inv, p_capacity); - return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity); + _FORCE_INLINE_ static constexpr void _increment_mod(uint32_t &r_idx, const uint32_t p_capacity) { + r_idx++; + // `if` is faster than both fastmod and mod. + if (unlikely(r_idx == p_capacity)) { + r_idx = 0; + } + } + + static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_hash_idx, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { + const uint32_t original_idx = fastmod(p_hash, p_capacity_inv, p_capacity); + const uint32_t distance_idx = p_hash_idx - original_idx + p_capacity; + // At most p_capacity over 0, so we can use an if (faster than fastmod). + return distance_idx >= p_capacity ? distance_idx - p_capacity : distance_idx; } - bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { - if (keys == nullptr || num_elements == 0) { + bool _lookup_key_idx(const TKey &p_key, uint32_t &r_key_idx) const { + if (_keys == nullptr || _size == 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]; + const uint32_t capacity = hash_table_size_primes[_capacity_idx]; + const uint64_t capacity_inv = hash_table_size_primes_inv[_capacity_idx]; uint32_t hash = _hash(p_key); - uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t hash_idx = fastmod(hash, capacity_inv, capacity); uint32_t distance = 0; while (true) { - if (hashes[pos] == EMPTY_HASH) { + if (_hashes[hash_idx] == EMPTY_HASH) { return false; } - if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { - return false; + if (_hashes[hash_idx] == hash && Comparator::compare(_keys[_hash_idx_to_key_idx[hash_idx]], p_key)) { + r_key_idx = _hash_idx_to_key_idx[hash_idx]; + return true; } - if (hashes[pos] == hash && Comparator::compare(keys[hash_to_key[pos]], p_key)) { - r_pos = hash_to_key[pos]; - return true; + if (distance > _get_probe_length(hash_idx, _hashes[hash_idx], capacity, capacity_inv)) { + return false; } - pos = fastmod(pos + 1, capacity_inv, capacity); + _increment_mod(hash_idx, capacity); distance++; } } - uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) { - const uint32_t capacity = hash_table_size_primes[capacity_index]; - const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_key_idx) { + const uint32_t capacity = hash_table_size_primes[_capacity_idx]; + const uint64_t capacity_inv = hash_table_size_primes_inv[_capacity_idx]; uint32_t hash = p_hash; - uint32_t index = p_index; + uint32_t key_idx = p_key_idx; uint32_t distance = 0; - uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t hash_idx = fastmod(hash, capacity_inv, capacity); while (true) { - if (hashes[pos] == EMPTY_HASH) { - hashes[pos] = hash; - key_to_hash[index] = pos; - hash_to_key[pos] = index; - return pos; + if (_hashes[hash_idx] == EMPTY_HASH) { + _hashes[hash_idx] = hash; + _key_idx_to_hash_idx[key_idx] = hash_idx; + _hash_idx_to_key_idx[hash_idx] = key_idx; + return hash_idx; } // Not an empty slot, let's check the probing length of the existing one. - uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity, capacity_inv); + uint32_t existing_probe_len = _get_probe_length(hash_idx, _hashes[hash_idx], capacity, capacity_inv); if (existing_probe_len < distance) { - key_to_hash[index] = pos; - SWAP(hash, hashes[pos]); - SWAP(index, hash_to_key[pos]); + _key_idx_to_hash_idx[key_idx] = hash_idx; + SWAP(hash, _hashes[hash_idx]); + SWAP(key_idx, _hash_idx_to_key_idx[hash_idx]); distance = existing_probe_len; } - pos = fastmod(pos + 1, capacity_inv, capacity); + _increment_mod(hash_idx, capacity); distance++; } } - void _resize_and_rehash(uint32_t p_new_capacity_index) { + void _resize_and_rehash(uint32_t p_new_capacity_idx) { // Capacity can't be 0. - capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index); - - uint32_t capacity = hash_table_size_primes[capacity_index]; + _capacity_idx = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_idx); - uint32_t *old_hashes = hashes; - uint32_t *old_key_to_hash = key_to_hash; + uint32_t capacity = hash_table_size_primes[_capacity_idx]; - hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - keys = reinterpret_cast(Memory::realloc_static(keys, sizeof(TKey) * capacity)); - key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - hash_to_key = reinterpret_cast(Memory::realloc_static(hash_to_key, sizeof(uint32_t) * capacity)); + uint32_t *old_hashes = _hashes; + uint32_t *old_key_to_hash = _key_idx_to_hash_idx; - for (uint32_t i = 0; i < capacity; i++) { - hashes[i] = EMPTY_HASH; - } + static_assert(EMPTY_HASH == 0, "Assuming EMPTY_HASH = 0 for alloc_static_zeroed call"); + _hashes = reinterpret_cast(Memory::alloc_static_zeroed(sizeof(uint32_t) * capacity)); + _keys = reinterpret_cast(Memory::realloc_static(_keys, sizeof(TKey) * capacity)); + _key_idx_to_hash_idx = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + _hash_idx_to_key_idx = reinterpret_cast(Memory::realloc_static(_hash_idx_to_key_idx, sizeof(uint32_t) * capacity)); - for (uint32_t i = 0; i < num_elements; i++) { + for (uint32_t i = 0; i < _size; i++) { uint32_t h = old_hashes[old_key_to_hash[i]]; _insert_with_hash(h, i); } @@ -167,130 +172,129 @@ class HashSet { Memory::free_static(old_key_to_hash); } + // Returns key index. _FORCE_INLINE_ int32_t _insert(const TKey &p_key) { - uint32_t capacity = hash_table_size_primes[capacity_index]; - if (unlikely(keys == nullptr)) { + uint32_t capacity = hash_table_size_primes[_capacity_idx]; + if (unlikely(_keys == nullptr)) { // Allocate on demand to save memory. - hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); - key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - hash_to_key = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - - for (uint32_t i = 0; i < capacity; i++) { - hashes[i] = EMPTY_HASH; - } + static_assert(EMPTY_HASH == 0, "Assuming EMPTY_HASH = 0 for alloc_static_zeroed call"); + _hashes = reinterpret_cast(Memory::alloc_static_zeroed(sizeof(uint32_t) * capacity)); + _keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); + _key_idx_to_hash_idx = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + _hash_idx_to_key_idx = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); } - uint32_t pos = 0; - bool exists = _lookup_pos(p_key, pos); + uint32_t key_idx = 0; + bool exists = _lookup_key_idx(p_key, key_idx); if (exists) { - return pos; + return key_idx; } else { - if (num_elements + 1 > MAX_OCCUPANCY * capacity) { - ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, -1, "Hash table maximum capacity reached, aborting insertion."); - _resize_and_rehash(capacity_index + 1); + if (_size + 1 > MAX_OCCUPANCY * capacity) { + ERR_FAIL_COND_V_MSG(_capacity_idx + 1 == HASH_TABLE_SIZE_MAX, -1, "Hash table maximum capacity reached, aborting insertion."); + _resize_and_rehash(_capacity_idx + 1); } uint32_t hash = _hash(p_key); - memnew_placement(&keys[num_elements], TKey(p_key)); - _insert_with_hash(hash, num_elements); - num_elements++; - return num_elements - 1; + memnew_placement(&_keys[_size], TKey(p_key)); + _insert_with_hash(hash, _size); + _size++; + return _size - 1; } } void _init_from(const HashSet &p_other) { - capacity_index = p_other.capacity_index; - num_elements = p_other.num_elements; + _capacity_idx = p_other._capacity_idx; + _size = p_other._size; - if (p_other.num_elements == 0) { + if (p_other._size == 0) { return; } - uint32_t capacity = hash_table_size_primes[capacity_index]; + uint32_t capacity = hash_table_size_primes[_capacity_idx]; - hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); - key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - hash_to_key = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + _hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + _keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); + _key_idx_to_hash_idx = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + _hash_idx_to_key_idx = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); - for (uint32_t i = 0; i < num_elements; i++) { - memnew_placement(&keys[i], TKey(p_other.keys[i])); - key_to_hash[i] = p_other.key_to_hash[i]; + for (uint32_t i = 0; i < _size; i++) { + memnew_placement(&_keys[i], TKey(p_other._keys[i])); + _key_idx_to_hash_idx[i] = p_other._key_idx_to_hash_idx[i]; } for (uint32_t i = 0; i < capacity; i++) { - hashes[i] = p_other.hashes[i]; - hash_to_key[i] = p_other.hash_to_key[i]; + _hashes[i] = p_other._hashes[i]; + _hash_idx_to_key_idx[i] = p_other._hash_idx_to_key_idx[i]; } } public: - _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; } - _FORCE_INLINE_ uint32_t size() const { return num_elements; } + _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[_capacity_idx]; } + _FORCE_INLINE_ uint32_t size() const { return _size; } /* Standard Godot Container API */ bool is_empty() const { - return num_elements == 0; + return _size == 0; } void clear() { - if (keys == nullptr || num_elements == 0) { + if (_keys == nullptr || _size == 0) { return; } - uint32_t capacity = hash_table_size_primes[capacity_index]; - for (uint32_t i = 0; i < capacity; i++) { - hashes[i] = EMPTY_HASH; - } - for (uint32_t i = 0; i < num_elements; i++) { - keys[i].~TKey(); + + uint32_t capacity = hash_table_size_primes[_capacity_idx]; + memset(_hashes, EMPTY_HASH, sizeof(EMPTY_HASH) * capacity); + + if constexpr (!std::is_trivially_destructible_v) { + for (uint32_t i = 0; i < _size; i++) { + _keys[i].~TKey(); + } } - num_elements = 0; + _size = 0; } _FORCE_INLINE_ bool has(const TKey &p_key) const { - uint32_t _pos = 0; - return _lookup_pos(p_key, _pos); + uint32_t _idx = 0; + return _lookup_key_idx(p_key, _idx); } bool erase(const TKey &p_key) { - uint32_t pos = 0; - bool exists = _lookup_pos(p_key, pos); + uint32_t key_idx = 0; + bool exists = _lookup_key_idx(p_key, key_idx); if (!exists) { return false; } - uint32_t key_pos = pos; - pos = key_to_hash[pos]; //make hash pos + uint32_t hash_idx = _key_idx_to_hash_idx[key_idx]; - const uint32_t capacity = hash_table_size_primes[capacity_index]; - const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; - uint32_t next_pos = fastmod(pos + 1, capacity_inv, capacity); - while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { - uint32_t kpos = hash_to_key[pos]; - uint32_t kpos_next = hash_to_key[next_pos]; - SWAP(key_to_hash[kpos], key_to_hash[kpos_next]); - SWAP(hashes[next_pos], hashes[pos]); - SWAP(hash_to_key[next_pos], hash_to_key[pos]); + const uint32_t capacity = hash_table_size_primes[_capacity_idx]; + const uint64_t capacity_inv = hash_table_size_primes_inv[_capacity_idx]; + uint32_t next_hash_idx = fastmod(hash_idx + 1, capacity_inv, capacity); + while (_hashes[next_hash_idx] != EMPTY_HASH && _get_probe_length(next_hash_idx, _hashes[next_hash_idx], capacity, capacity_inv) != 0) { + uint32_t cur_key_idx = _hash_idx_to_key_idx[hash_idx]; + uint32_t next_key_idx = _hash_idx_to_key_idx[next_hash_idx]; + SWAP(_key_idx_to_hash_idx[cur_key_idx], _key_idx_to_hash_idx[next_key_idx]); + SWAP(_hashes[next_hash_idx], _hashes[hash_idx]); + SWAP(_hash_idx_to_key_idx[next_hash_idx], _hash_idx_to_key_idx[hash_idx]); - pos = next_pos; - next_pos = fastmod(pos + 1, capacity_inv, capacity); + hash_idx = next_hash_idx; + _increment_mod(next_hash_idx, capacity); } - hashes[pos] = EMPTY_HASH; - keys[key_pos].~TKey(); - num_elements--; - if (key_pos < num_elements) { - // Not the last key, move the last one here to keep keys lineal - memnew_placement(&keys[key_pos], TKey(keys[num_elements])); - keys[num_elements].~TKey(); - key_to_hash[key_pos] = key_to_hash[num_elements]; - hash_to_key[key_to_hash[num_elements]] = key_pos; + _hashes[hash_idx] = EMPTY_HASH; + _keys[key_idx].~TKey(); + _size--; + if (key_idx < _size) { + // Not the last key, move the last one here to keep keys contiguous. + memnew_placement(&_keys[key_idx], TKey(_keys[_size])); + _keys[_size].~TKey(); + _key_idx_to_hash_idx[key_idx] = _key_idx_to_hash_idx[_size]; + _hash_idx_to_key_idx[_key_idx_to_hash_idx[_size]] = key_idx; } return true; @@ -299,102 +303,105 @@ class HashSet { // Reserves space for a number of elements, useful to avoid many resizes and rehashes. // If adding a known (possibly large) number of elements at once, must be larger than old capacity. void reserve(uint32_t p_new_capacity) { - uint32_t new_index = capacity_index; + uint32_t new_capacity_idx = _capacity_idx; - while (hash_table_size_primes[new_index] < p_new_capacity) { - ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr); - new_index++; + while (hash_table_size_primes[new_capacity_idx] < p_new_capacity) { + ERR_FAIL_COND_MSG(new_capacity_idx + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr); + new_capacity_idx++; } - if (new_index == capacity_index) { + if (new_capacity_idx == _capacity_idx) { + if (p_new_capacity < _size) { + WARN_VERBOSE("reserve() called with a capacity smaller than the current size. This is likely a mistake."); + } return; } - if (keys == nullptr) { - capacity_index = new_index; + if (_keys == nullptr) { + _capacity_idx = new_capacity_idx; return; // Unallocated yet. } - _resize_and_rehash(new_index); + _resize_and_rehash(new_capacity_idx); } /** Iterator API **/ struct Iterator { _FORCE_INLINE_ const TKey &operator*() const { - return keys[index]; + return _keys[_key_idx]; } _FORCE_INLINE_ const TKey *operator->() const { - return &keys[index]; + return &_keys[_key_idx]; } _FORCE_INLINE_ Iterator &operator++() { - index++; - if (index >= (int32_t)num_keys) { - index = -1; - keys = nullptr; - num_keys = 0; + _key_idx++; + if (_key_idx >= (int32_t)_num_keys) { + _key_idx = -1; + _keys = nullptr; + _num_keys = 0; } return *this; } _FORCE_INLINE_ Iterator &operator--() { - index--; - if (index < 0) { - index = -1; - keys = nullptr; - num_keys = 0; + _key_idx--; + if (_key_idx < 0) { + _key_idx = -1; + _keys = nullptr; + _num_keys = 0; } return *this; } - _FORCE_INLINE_ bool operator==(const Iterator &b) const { return keys == b.keys && index == b.index; } - _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return keys != b.keys || index != b.index; } + _FORCE_INLINE_ bool operator==(const Iterator &p_other) const { return _keys == p_other._keys && _key_idx == p_other._key_idx; } + _FORCE_INLINE_ bool operator!=(const Iterator &p_other) const { return _keys != p_other._keys || _key_idx != p_other._key_idx; } _FORCE_INLINE_ explicit operator bool() const { - return keys != nullptr; + return _keys != nullptr; } - _FORCE_INLINE_ Iterator(const TKey *p_keys, uint32_t p_num_keys, int32_t p_index = -1) { - keys = p_keys; - num_keys = p_num_keys; - index = p_index; + _FORCE_INLINE_ Iterator(const TKey *p_keys, uint32_t p_num_keys, int32_t p_key_idx = -1) { + _keys = p_keys; + _num_keys = p_num_keys; + _key_idx = p_key_idx; } _FORCE_INLINE_ Iterator() {} _FORCE_INLINE_ Iterator(const Iterator &p_it) { - keys = p_it.keys; - num_keys = p_it.num_keys; - index = p_it.index; + _keys = p_it._keys; + _num_keys = p_it._num_keys; + _key_idx = p_it._key_idx; } _FORCE_INLINE_ void operator=(const Iterator &p_it) { - keys = p_it.keys; - num_keys = p_it.num_keys; - index = p_it.index; + _keys = p_it._keys; + _num_keys = p_it._num_keys; + _key_idx = p_it._key_idx; } private: - const TKey *keys = nullptr; - uint32_t num_keys = 0; - int32_t index = -1; + const TKey *_keys = nullptr; + uint32_t _num_keys = 0; + int32_t _key_idx = -1; }; - _FORCE_INLINE_ Iterator begin() const { - return num_elements ? Iterator(keys, num_elements, 0) : Iterator(); + _FORCE_INLINE_ Iterator begin() const _LIFETIME_BOUND_ { + return _size ? Iterator(_keys, _size, 0) : Iterator(); } - _FORCE_INLINE_ Iterator end() const { + _FORCE_INLINE_ Iterator end() const _LIFETIME_BOUND_ { return Iterator(); } - _FORCE_INLINE_ Iterator last() const { - if (num_elements == 0) { + _FORCE_INLINE_ Iterator last() const _LIFETIME_BOUND_ { + if (_size == 0) { return Iterator(); } - return Iterator(keys, num_elements, num_elements - 1); + return Iterator(_keys, _size, _size - 1); } - _FORCE_INLINE_ Iterator find(const TKey &p_key) const { - uint32_t pos = 0; - bool exists = _lookup_pos(p_key, pos); + _FORCE_INLINE_ Iterator find(const TKey &p_key) const _LIFETIME_BOUND_ { + uint32_t key_idx = 0; + bool exists = _lookup_key_idx(p_key, key_idx); if (!exists) { return end(); } - return Iterator(keys, num_elements, pos); + return Iterator(_keys, _size, key_idx); } _FORCE_INLINE_ void remove(const Iterator &p_iter) { @@ -405,17 +412,35 @@ class HashSet { /* Insert */ - Iterator insert(const TKey &p_key) { - uint32_t pos = _insert(p_key); - return Iterator(keys, num_elements, pos); + Iterator insert(const TKey &p_key) _LIFETIME_BOUND_ { + uint32_t key_idx = _insert(p_key); + return Iterator(_keys, _size, key_idx); } /* Constructors */ - HashSet(const HashSet &p_other) { + explicit HashSet(const HashSet &p_other) { _init_from(p_other); } + HashSet(HashSet &&p_other) { + _keys = p_other._keys; + _hash_idx_to_key_idx = p_other._hash_idx_to_key_idx; + _key_idx_to_hash_idx = p_other._key_idx_to_hash_idx; + _hashes = p_other._hashes; + + _capacity_idx = p_other._capacity_idx; + _size = p_other._size; + + p_other._keys = nullptr; + p_other._hash_idx_to_key_idx = nullptr; + p_other._hashes = nullptr; + p_other._key_idx_to_hash_idx = nullptr; + + p_other._capacity_idx = 0; + p_other._size = 0; + } + void operator=(const HashSet &p_other) { if (this == &p_other) { return; // Ignore self assignment. @@ -423,29 +448,57 @@ class HashSet { clear(); - if (keys != nullptr) { - Memory::free_static(keys); - Memory::free_static(key_to_hash); - Memory::free_static(hash_to_key); - Memory::free_static(hashes); - keys = nullptr; - hashes = nullptr; - hash_to_key = nullptr; - key_to_hash = nullptr; + if (_keys != nullptr) { + Memory::free_static(_keys); + Memory::free_static(_key_idx_to_hash_idx); + Memory::free_static(_hash_idx_to_key_idx); + Memory::free_static(_hashes); + _keys = nullptr; + _hashes = nullptr; + _hash_idx_to_key_idx = nullptr; + _key_idx_to_hash_idx = nullptr; } _init_from(p_other); } + void operator=(HashSet &&p_other) { + if (this == &p_other) { + return; // Ignore self assignment. + } + + SWAP(_keys, p_other._keys); + SWAP(_hash_idx_to_key_idx, p_other._hash_idx_to_key_idx); + SWAP(_key_idx_to_hash_idx, p_other._key_idx_to_hash_idx); + SWAP(_hashes, p_other._hashes); + + SWAP(_capacity_idx, p_other._capacity_idx); + SWAP(_size, p_other._size); + } + + bool operator==(const HashSet &p_other) const { + if (_size != p_other._size) { + return false; + } + for (uint32_t i = 0; i < _size; i++) { + if (!p_other.has(_keys[i])) { + return false; + } + } + return true; + } + bool operator!=(const HashSet &p_other) const { + return !(*this == p_other); + } + HashSet(uint32_t p_initial_capacity) { // Capacity can't be 0. - capacity_index = 0; + _capacity_idx = 0; reserve(p_initial_capacity); } HashSet() { - capacity_index = MIN_CAPACITY_INDEX; + _capacity_idx = MIN_CAPACITY_INDEX; } - HashSet(std::initializer_list p_init) { reserve(p_init.size()); for (const TKey &E : p_init) { @@ -456,27 +509,27 @@ class HashSet { void reset() { clear(); - if (keys != nullptr) { - Memory::free_static(keys); - Memory::free_static(key_to_hash); - Memory::free_static(hash_to_key); - Memory::free_static(hashes); - keys = nullptr; - hashes = nullptr; - hash_to_key = nullptr; - key_to_hash = nullptr; - } - capacity_index = MIN_CAPACITY_INDEX; + if (_keys != nullptr) { + Memory::free_static(_keys); + Memory::free_static(_key_idx_to_hash_idx); + Memory::free_static(_hash_idx_to_key_idx); + Memory::free_static(_hashes); + _keys = nullptr; + _hashes = nullptr; + _hash_idx_to_key_idx = nullptr; + _key_idx_to_hash_idx = nullptr; + } + _capacity_idx = MIN_CAPACITY_INDEX; } ~HashSet() { clear(); - if (keys != nullptr) { - Memory::free_static(keys); - Memory::free_static(key_to_hash); - Memory::free_static(hash_to_key); - Memory::free_static(hashes); + if (_keys != nullptr) { + Memory::free_static(_keys); + Memory::free_static(_key_idx_to_hash_idx); + Memory::free_static(_hash_idx_to_key_idx); + Memory::free_static(_hashes); } } }; diff --git a/include/godot_cpp/templates/hashfuncs.hpp b/include/godot_cpp/templates/hashfuncs.hpp index 1371f85fd..69ca63a6b 100644 --- a/include/godot_cpp/templates/hashfuncs.hpp +++ b/include/godot_cpp/templates/hashfuncs.hpp @@ -30,9 +30,8 @@ #pragma once -// Needed for fastmod. -#if defined(_MSC_VER) -#include +#ifdef _MSC_VER +#include // Needed for `__umulh` below. #endif #include @@ -58,13 +57,14 @@ */ namespace godot { - +template +struct Pair; /** * DJB2 Hash function * @param C String * @return 32-bits hashcode */ -static _FORCE_INLINE_ uint32_t hash_djb2(const char *p_cstr) { +_FORCE_INLINE_ uint32_t hash_djb2(const char *p_cstr) { const unsigned char *chr = (const unsigned char *)p_cstr; uint32_t hash = 5381; uint32_t c = *chr++; @@ -77,7 +77,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2(const char *p_cstr) { return hash; } -static _FORCE_INLINE_ uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len, uint32_t p_prev = 5381) { +_FORCE_INLINE_ uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len, uint32_t p_prev = 5381) { uint32_t hash = p_prev; for (int i = 0; i < p_len; i++) { @@ -87,7 +87,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len return hash; } -static _FORCE_INLINE_ uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) { +_FORCE_INLINE_ uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) { return ((p_prev << 5) + p_prev) ^ p_in; } @@ -98,7 +98,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = * @param p_int - 64-bit unsigned integer key to be hashed * @return unsigned 32-bit value representing hashcode */ -static _FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) { +_FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) { uint64_t v = p_int; v = (~v) + (v << 18); // v = (v << 18) - v - 1; v = v ^ (v >> 31); @@ -109,21 +109,21 @@ static _FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) { return uint32_t(v); } -static _FORCE_INLINE_ uint64_t hash64_murmur3_64(uint64_t key, uint64_t seed) { - key ^= seed; - key ^= key >> 33; - key *= 0xff51afd7ed558ccd; - key ^= key >> 33; - key *= 0xc4ceb9fe1a85ec53; - key ^= key >> 33; - return key; +_FORCE_INLINE_ uint64_t hash64_murmur3_64(uint64_t p_key, uint64_t p_seed) { + p_key ^= p_seed; + p_key ^= p_key >> 33; + p_key *= 0xff51afd7ed558ccd; + p_key ^= p_key >> 33; + p_key *= 0xc4ceb9fe1a85ec53; + p_key ^= p_key >> 33; + return p_key; } #define HASH_MURMUR3_SEED 0x7F07C65 // Murmurhash3 32-bit version. // All MurmurHash versions are public domain software, and the author disclaims all copyright to their code. -static _FORCE_INLINE_ uint32_t hash_murmur3_one_32(uint32_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { +_FORCE_INLINE_ uint32_t hash_murmur3_one_32(uint32_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { p_in *= 0xcc9e2d51; p_in = (p_in << 15) | (p_in >> 17); p_in *= 0x1b873593; @@ -135,48 +135,15 @@ static _FORCE_INLINE_ uint32_t hash_murmur3_one_32(uint32_t p_in, uint32_t p_see return p_seed; } -static _FORCE_INLINE_ uint32_t hash_murmur3_one_float(float p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { - union { - float f; - uint32_t i; - } u; - - // Normalize +/- 0.0 and NaN values so they hash the same. - if (p_in == 0.0f) { - u.f = 0.0; - } else if (Math::is_nan(p_in)) { - u.f = Math::NaN; - } else { - u.f = p_in; - } - - return hash_murmur3_one_32(u.i, p_seed); -} - -static _FORCE_INLINE_ uint32_t hash_murmur3_one_64(uint64_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { +_FORCE_INLINE_ uint32_t hash_murmur3_one_64(uint64_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { p_seed = hash_murmur3_one_32(p_in & 0xFFFFFFFF, p_seed); return hash_murmur3_one_32(p_in >> 32, p_seed); } -static _FORCE_INLINE_ uint32_t hash_murmur3_one_double(double p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { - union { - double d; - uint64_t i; - } u; - - // Normalize +/- 0.0 and NaN values so they hash the same. - if (p_in == 0.0f) { - u.d = 0.0; - } else if (Math::is_nan(p_in)) { - u.d = Math::NaN; - } else { - u.d = p_in; - } - - return hash_murmur3_one_64(u.i, p_seed); -} +uint32_t hash_murmur3_one_float(float p_in, uint32_t p_seed = HASH_MURMUR3_SEED); +uint32_t hash_murmur3_one_double(double p_in, uint32_t p_seed = HASH_MURMUR3_SEED); -static _FORCE_INLINE_ uint32_t hash_murmur3_one_real(real_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { +_FORCE_INLINE_ uint32_t hash_murmur3_one_real(real_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { #ifdef REAL_T_IS_DOUBLE return hash_murmur3_one_double(p_in, p_seed); #else @@ -184,130 +151,37 @@ static _FORCE_INLINE_ uint32_t hash_murmur3_one_real(real_t p_in, uint32_t p_see #endif } -static _FORCE_INLINE_ uint32_t hash_rotl32(uint32_t x, int8_t r) { - return (x << r) | (x >> (32 - r)); +_FORCE_INLINE_ uint32_t hash_rotl32(uint32_t p_x, int8_t p_r) { + return (p_x << p_r) | (p_x >> (32 - p_r)); } -static _FORCE_INLINE_ uint32_t hash_fmix32(uint32_t h) { - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; +_FORCE_INLINE_ uint32_t hash_fmix32(uint32_t p_h) { + p_h ^= p_h >> 16; + p_h *= 0x85ebca6b; + p_h ^= p_h >> 13; + p_h *= 0xc2b2ae35; + p_h ^= p_h >> 16; - return h; + return p_h; } -static _FORCE_INLINE_ uint32_t hash_murmur3_buffer(const void *key, int length, const uint32_t seed = HASH_MURMUR3_SEED) { - // Although not required, this is a random prime number. - const uint8_t *data = (const uint8_t *)key; - const int nblocks = length / 4; - - uint32_t h1 = seed; - - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; +uint32_t hash_murmur3_buffer(const void *p_key, int p_length, uint32_t p_seed = HASH_MURMUR3_SEED); - const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); +uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381); +uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 5381); - for (int i = -nblocks; i; i++) { - uint32_t k1 = blocks[i]; - - k1 *= c1; - k1 = hash_rotl32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = hash_rotl32(h1, 13); - h1 = h1 * 5 + 0xe6546b64; - } - - const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); - - uint32_t k1 = 0; - - switch (length & 3) { - case 3: - k1 ^= tail[2] << 16; - [[fallthrough]]; - case 2: - k1 ^= tail[1] << 8; - [[fallthrough]]; - case 1: - k1 ^= tail[0]; - k1 *= c1; - k1 = hash_rotl32(k1, 15); - k1 *= c2; - h1 ^= k1; - }; - - // Finalize with additional bit mixing. - h1 ^= length; - return hash_fmix32(h1); +_FORCE_INLINE_ uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) { + return ((p_prev << 5) + p_prev) ^ p_in; } -static _FORCE_INLINE_ uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) { - union { - double d; - uint64_t i; - } u; - - // Normalize +/- 0.0 and NaN values so they hash the same. - if (p_in == 0.0f) { - u.d = 0.0; - } else if (Math::is_nan(p_in)) { - u.d = Math::NaN; - } else { - u.d = p_in; - } - - return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i); -} +template > +struct has_hash_method : std::false_type {}; template -static _FORCE_INLINE_ uint32_t hash_make_uint32_t(T p_in) { - union { - T t; - uint32_t _u32; - } _u; - _u._u32 = 0; - _u.t = p_in; - return _u._u32; -} - -static _FORCE_INLINE_ uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 5381) { - union { - double d; - uint64_t i; - } u; - - // Normalize +/- 0.0 and NaN values so they hash the same. - if (p_in == 0.0f) { - u.d = 0.0; - } else if (Math::is_nan(p_in)) { - u.d = Math::NaN; - } else { - u.d = p_in; - } - - return ((p_prev << 5) + p_prev) + u.i; -} - -static _FORCE_INLINE_ uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) { - return ((p_prev << 5) + p_prev) ^ p_in; -} +struct has_hash_method().hash()), uint32_t>>> : std::true_type {}; template -static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) { - union { - T t; - uint64_t _u64; - } _u; - _u._u64 = 0; // in case p_in is smaller - - _u.t = p_in; - return _u._u64; -} +constexpr bool has_hash_method_v = has_hash_method::value; template class Ref; @@ -417,20 +291,23 @@ struct HashMapHasherDefault { } }; -struct HashHasher { - static _FORCE_INLINE_ uint32_t hash(const int32_t hash) { return hash; } - static _FORCE_INLINE_ uint32_t hash(const uint32_t hash) { return hash; } - static _FORCE_INLINE_ uint64_t hash(const int64_t hash) { return hash; } - static _FORCE_INLINE_ uint64_t hash(const uint64_t hash) { return hash; } -}; +template > +struct has_is_same_method : std::false_type {}; -// TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed template -struct HashableHasher { - static _FORCE_INLINE_ uint32_t hash(const T &hashable) { return hashable.hash(); } -}; +struct has_is_same_method().is_same(std::declval())), uint32_t>>> : std::true_type {}; template +constexpr bool has_is_same_method_v = has_is_same_method::value; + +struct HashHasher { + static _FORCE_INLINE_ uint32_t hash(const int32_t p_hash) { return p_hash; } + static _FORCE_INLINE_ uint32_t hash(const uint32_t p_hash) { return p_hash; } + static _FORCE_INLINE_ uint64_t hash(const int64_t p_hash) { return p_hash; } + static _FORCE_INLINE_ uint64_t hash(const uint64_t p_hash) { return p_hash; } +}; + +template struct HashMapComparatorDefault { static bool compare(const T &p_lhs, const T &p_rhs) { return p_lhs == p_rhs; @@ -440,116 +317,22 @@ struct HashMapComparatorDefault { template <> struct HashMapComparatorDefault { static bool compare(const float &p_lhs, const float &p_rhs) { - return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); + return Math::is_same(p_lhs, p_rhs); } }; template <> struct HashMapComparatorDefault { static bool compare(const double &p_lhs, const double &p_rhs) { - return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Color &p_lhs, const Color &p_rhs) { - return ((p_lhs.r == p_rhs.r) || (Math::is_nan(p_lhs.r) && Math::is_nan(p_rhs.r))) && ((p_lhs.g == p_rhs.g) || (Math::is_nan(p_lhs.g) && Math::is_nan(p_rhs.g))) && ((p_lhs.b == p_rhs.b) || (Math::is_nan(p_lhs.b) && Math::is_nan(p_rhs.b))) && ((p_lhs.a == p_rhs.a) || (Math::is_nan(p_lhs.a) && Math::is_nan(p_rhs.a))); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) { - return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) { - return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))); + return Math::is_same(p_lhs, p_rhs); } }; -template <> -struct HashMapComparatorDefault { - static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) { - return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w))); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) { - return HashMapComparatorDefault().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault().compare(p_lhs.size, p_rhs.size); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const AABB &p_lhs, const AABB &p_rhs) { - return HashMapComparatorDefault().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault().compare(p_lhs.size, p_rhs.size); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Plane &p_lhs, const Plane &p_rhs) { - return HashMapComparatorDefault().compare(p_lhs.normal, p_rhs.normal) && ((p_lhs.d == p_rhs.d) || (Math::is_nan(p_lhs.d) && Math::is_nan(p_rhs.d))); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) { - for (int i = 0; i < 3; ++i) { - if (!HashMapComparatorDefault().compare(p_lhs.columns[i], p_rhs.columns[i])) { - return false; - } - } - - return true; - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Basis &p_lhs, const Basis &p_rhs) { - for (int i = 0; i < 3; ++i) { - if (!HashMapComparatorDefault().compare(p_lhs.rows[i], p_rhs.rows[i])) { - return false; - } - } - - return true; - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) { - return HashMapComparatorDefault().compare(p_lhs.basis, p_rhs.basis) && HashMapComparatorDefault().compare(p_lhs.origin, p_rhs.origin); - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Projection &p_lhs, const Projection &p_rhs) { - for (int i = 0; i < 4; ++i) { - if (!HashMapComparatorDefault().compare(p_lhs.columns[i], p_rhs.columns[i])) { - return false; - } - } - - return true; - } -}; - -template <> -struct HashMapComparatorDefault { - static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) { - return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w))); +template +struct HashMapComparatorDefault>> { + // For self comparing types. + static bool compare(const T &p_lhs, const T &p_rhs) { + return p_lhs.is_same(p_rhs); } }; @@ -626,25 +409,25 @@ inline constexpr uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = { * Faster Remainder by Direct Computation: Applications to Compilers and Software Libraries * https://arxiv.org/abs/1902.01961 */ -static _FORCE_INLINE_ uint32_t fastmod(const uint32_t n, const uint64_t c, const uint32_t d) { +static _FORCE_INLINE_ uint32_t fastmod(const uint32_t p_n, const uint64_t p_c, const uint32_t p_d) { #if defined(_MSC_VER) // Returns the upper 64 bits of the product of two 64-bit unsigned integers. // This intrinsic function is required since MSVC does not support unsigned 128-bit integers. #if defined(_M_X64) || defined(_M_ARM64) - return __umulh(c * n, d); + return __umulh(p_c * p_n, p_d); #else // Fallback to the slower method for 32-bit platforms. - return n % d; + return p_n % p_d; #endif // _M_X64 || _M_ARM64 #else #ifdef __SIZEOF_INT128__ // Prevent compiler warning, because we know what we are doing. - uint64_t lowbits = c * n; + uint64_t lowbits = p_c * p_n; __extension__ typedef unsigned __int128 uint128; - return static_cast(((uint128)lowbits * d) >> 64); + return static_cast(((uint128)lowbits * p_d) >> 64); #else // Fallback to the slower method if no 128-bit unsigned integer type is available. - return n % d; + return p_n % p_d; #endif // __SIZEOF_INT128__ #endif // _MSC_VER } diff --git a/include/godot_cpp/templates/list.hpp b/include/godot_cpp/templates/list.hpp index 9291f5e40..1b6a9b103 100644 --- a/include/godot_cpp/templates/list.hpp +++ b/include/godot_cpp/templates/list.hpp @@ -32,22 +32,23 @@ #include #include -#include +#include #include /** - * Generic Templatized Linked List Implementation. - * The implementation differs from the STL one because - * a compatible preallocated linked list can be written - * using the same API, or features such as erasing an element - * from the iterator. + * Ordered storage implemented as a doubly linked list. + * + * Elements are pointer stable. + * + * Core container guidance: + * https://docs.godotengine.org/en/latest/engine_details/architecture/core_types.html#containers */ namespace godot { template -class List { +class _WARN_UNUSED_ List { struct _Data; public: @@ -136,8 +137,6 @@ class List { } void transfer_to_back(List *p_dst_list); - - _FORCE_INLINE_ Element() {} }; typedef T ValueType; @@ -156,10 +155,10 @@ class List { return *this; } - _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; } - _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; } + _FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return E == p_other.E; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return E != p_other.E; } - _FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; } + _FORCE_INLINE_ ConstIterator(const Element *p_element) { E = p_element; } _FORCE_INLINE_ ConstIterator() {} _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; } @@ -181,10 +180,10 @@ class List { return *this; } - _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; } - _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; } + _FORCE_INLINE_ bool operator==(const Iterator &p_other) const { return E == p_other.E; } + _FORCE_INLINE_ bool operator!=(const Iterator &p_other) const { return E != p_other.E; } - Iterator(Element *p_E) { E = p_E; } + Iterator(Element *p_element) { E = p_element; } Iterator() {} Iterator(const Iterator &p_it) { E = p_it.E; } @@ -227,27 +226,27 @@ class List { Element *last = nullptr; int size_cache = 0; - bool erase(Element *p_I) { - ERR_FAIL_NULL_V(p_I, false); - ERR_FAIL_COND_V(p_I->data != this, false); + bool erase(Element *p_element) { + ERR_FAIL_NULL_V(p_element, false); + ERR_FAIL_COND_V(p_element->data != this, false); - if (first == p_I) { - first = p_I->next_ptr; + if (first == p_element) { + first = p_element->next_ptr; } - if (last == p_I) { - last = p_I->prev_ptr; + if (last == p_element) { + last = p_element->prev_ptr; } - if (p_I->prev_ptr) { - p_I->prev_ptr->next_ptr = p_I->next_ptr; + if (p_element->prev_ptr) { + p_element->prev_ptr->next_ptr = p_element->next_ptr; } - if (p_I->next_ptr) { - p_I->next_ptr->prev_ptr = p_I->prev_ptr; + if (p_element->next_ptr) { + p_element->next_ptr->prev_ptr = p_element->prev_ptr; } - memdelete_allocator(p_I); + memdelete_allocator(p_element); size_cache--; return true; @@ -260,35 +259,35 @@ class List { /** * return a const iterator to the beginning of the list. */ - _FORCE_INLINE_ const Element *front() const { + _FORCE_INLINE_ const Element *front() const _LIFETIME_BOUND_ { return _data ? _data->first : nullptr; } /** * return an iterator to the beginning of the list. */ - _FORCE_INLINE_ Element *front() { + _FORCE_INLINE_ Element *front() _LIFETIME_BOUND_ { return _data ? _data->first : nullptr; } /** * return a const iterator to the last member of the list. */ - _FORCE_INLINE_ const Element *back() const { + _FORCE_INLINE_ const Element *back() const _LIFETIME_BOUND_ { return _data ? _data->last : nullptr; } /** * return an iterator to the last member of the list. */ - _FORCE_INLINE_ Element *back() { + _FORCE_INLINE_ Element *back() _LIFETIME_BOUND_ { return _data ? _data->last : nullptr; } /** * store a new element at the end of the list */ - Element *push_back(const T &value) { + Element *push_back(const T &p_value) _LIFETIME_BOUND_ { if (!_data) { _data = memnew_allocator(_Data, A); _data->first = nullptr; @@ -297,7 +296,7 @@ class List { } Element *n = memnew_allocator(Element, A); - n->value = (T &)value; + n->value = (T &)p_value; n->prev_ptr = _data->last; n->next_ptr = nullptr; @@ -327,7 +326,7 @@ class List { /** * store a new element at the beginning of the list */ - Element *push_front(const T &value) { + Element *push_front(const T &p_value) _LIFETIME_BOUND_ { if (!_data) { _data = memnew_allocator(_Data, A); _data->first = nullptr; @@ -336,7 +335,7 @@ class List { } Element *n = memnew_allocator(Element, A); - n->value = (T &)value; + n->value = (T &)p_value; n->prev_ptr = nullptr; n->next_ptr = _data->first; n->data = _data; @@ -362,7 +361,7 @@ class List { } } - Element *insert_after(Element *p_element, const T &p_value) { + Element *insert_after(Element *p_element, const T &p_value) _LIFETIME_BOUND_ { CRASH_COND(p_element && (!_data || p_element->data != _data)); if (!p_element) { @@ -388,7 +387,7 @@ class List { return n; } - Element *insert_before(Element *p_element, const T &p_value) { + Element *insert_before(Element *p_element, const T &p_value) _LIFETIME_BOUND_ { CRASH_COND(p_element && (!_data || p_element->data != _data)); if (!p_element) { @@ -418,7 +417,20 @@ class List { * find an element in the list, */ template - Element *find(const T_v &p_val) { + const Element *find(const T_v &p_val) const _LIFETIME_BOUND_ { + const Element *it = front(); + while (it) { + if (it->value == p_val) { + return it; + } + it = it->next(); + } + + return nullptr; + } + + template + Element *find(const T_v &p_val) _LIFETIME_BOUND_ { Element *it = front(); while (it) { if (it->value == p_val) { @@ -433,9 +445,9 @@ class List { /** * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. */ - bool erase(Element *p_I) { - if (_data && p_I) { - bool ret = _data->erase(p_I); + bool erase(Element *p_element) { + if (_data && p_element) { + bool ret = _data->erase(p_element); if (_data->size_cache == 0) { memdelete_allocator<_Data, A>(_data); @@ -451,8 +463,8 @@ class List { /** * erase the first element in the list, that contains value */ - bool erase(const T &value) { - Element *I = find(value); + bool erase(const T &p_value) { + Element *I = find(p_value); return erase(I); } @@ -476,43 +488,43 @@ class List { return _data ? _data->size_cache : 0; } - void swap(Element *p_A, Element *p_B) { - ERR_FAIL_COND(!p_A || !p_B); - ERR_FAIL_COND(p_A->data != _data); - ERR_FAIL_COND(p_B->data != _data); + void swap(Element *p_left, Element *p_right) { + ERR_FAIL_COND(!p_left || !p_right); + ERR_FAIL_COND(p_left->data != _data); + ERR_FAIL_COND(p_right->data != _data); - if (p_A == p_B) { + if (p_left == p_right) { return; } - Element *A_prev = p_A->prev_ptr; - Element *A_next = p_A->next_ptr; - Element *B_prev = p_B->prev_ptr; - Element *B_next = p_B->next_ptr; + Element *A_prev = p_left->prev_ptr; + Element *A_next = p_left->next_ptr; + Element *B_prev = p_right->prev_ptr; + Element *B_next = p_right->next_ptr; if (A_prev) { - A_prev->next_ptr = p_B; + A_prev->next_ptr = p_right; } else { - _data->first = p_B; + _data->first = p_right; } if (B_prev) { - B_prev->next_ptr = p_A; + B_prev->next_ptr = p_left; } else { - _data->first = p_A; + _data->first = p_left; } if (A_next) { - A_next->prev_ptr = p_B; + A_next->prev_ptr = p_right; } else { - _data->last = p_B; + _data->last = p_right; } if (B_next) { - B_next->prev_ptr = p_A; + B_next->prev_ptr = p_left; } else { - _data->last = p_A; + _data->last = p_left; } - p_A->prev_ptr = A_next == p_B ? p_B : B_prev; - p_A->next_ptr = B_next == p_A ? p_B : B_next; - p_B->prev_ptr = B_next == p_A ? p_A : A_prev; - p_B->next_ptr = A_next == p_B ? p_A : A_next; + p_left->prev_ptr = A_next == p_right ? p_right : B_prev; + p_left->next_ptr = B_next == p_left ? p_right : B_next; + p_right->prev_ptr = B_next == p_left ? p_left : A_prev; + p_right->next_ptr = A_next == p_right ? p_left : A_next; } /** * copy the list @@ -537,7 +549,7 @@ class List { // Random access to elements, use with care, // do not use for iteration. - T &get(int p_index) { + T &get(int p_index) _LIFETIME_BOUND_ { CRASH_BAD_INDEX(p_index, size()); Element *I = front(); @@ -552,7 +564,7 @@ class List { // Random access to elements, use with care, // do not use for iteration. - const T &get(int p_index) const { + const T &get(int p_index) const _LIFETIME_BOUND_ { CRASH_BAD_INDEX(p_index, size()); const Element *I = front(); @@ -565,30 +577,30 @@ class List { return I->get(); } - void move_to_back(Element *p_I) { - ERR_FAIL_COND(p_I->data != _data); - if (!p_I->next_ptr) { + void move_to_back(Element *p_element) { + ERR_FAIL_COND(p_element->data != _data); + if (!p_element->next_ptr) { return; } - if (_data->first == p_I) { - _data->first = p_I->next_ptr; + if (_data->first == p_element) { + _data->first = p_element->next_ptr; } - if (_data->last == p_I) { - _data->last = p_I->prev_ptr; + if (_data->last == p_element) { + _data->last = p_element->prev_ptr; } - if (p_I->prev_ptr) { - p_I->prev_ptr->next_ptr = p_I->next_ptr; + if (p_element->prev_ptr) { + p_element->prev_ptr->next_ptr = p_element->next_ptr; } - p_I->next_ptr->prev_ptr = p_I->prev_ptr; + p_element->next_ptr->prev_ptr = p_element->prev_ptr; - _data->last->next_ptr = p_I; - p_I->prev_ptr = _data->last; - p_I->next_ptr = nullptr; - _data->last = p_I; + _data->last->next_ptr = p_element; + p_element->prev_ptr = _data->last; + p_element->next_ptr = nullptr; + _data->last = p_element; } void reverse() { @@ -602,170 +614,81 @@ class List { } } - void move_to_front(Element *p_I) { - ERR_FAIL_COND(p_I->data != _data); - if (!p_I->prev_ptr) { + void move_to_front(Element *p_element) { + ERR_FAIL_COND(p_element->data != _data); + if (!p_element->prev_ptr) { return; } - if (_data->first == p_I) { - _data->first = p_I->next_ptr; + if (_data->first == p_element) { + _data->first = p_element->next_ptr; } - if (_data->last == p_I) { - _data->last = p_I->prev_ptr; + if (_data->last == p_element) { + _data->last = p_element->prev_ptr; } - p_I->prev_ptr->next_ptr = p_I->next_ptr; + p_element->prev_ptr->next_ptr = p_element->next_ptr; - if (p_I->next_ptr) { - p_I->next_ptr->prev_ptr = p_I->prev_ptr; + if (p_element->next_ptr) { + p_element->next_ptr->prev_ptr = p_element->prev_ptr; } - _data->first->prev_ptr = p_I; - p_I->next_ptr = _data->first; - p_I->prev_ptr = nullptr; - _data->first = p_I; + _data->first->prev_ptr = p_element; + p_element->next_ptr = _data->first; + p_element->prev_ptr = nullptr; + _data->first = p_element; } - void move_before(Element *value, Element *where) { - if (value->prev_ptr) { - value->prev_ptr->next_ptr = value->next_ptr; + void move_before(Element *p_value, Element *p_where) { + if (p_value->prev_ptr) { + p_value->prev_ptr->next_ptr = p_value->next_ptr; } else { - _data->first = value->next_ptr; + _data->first = p_value->next_ptr; } - if (value->next_ptr) { - value->next_ptr->prev_ptr = value->prev_ptr; + if (p_value->next_ptr) { + p_value->next_ptr->prev_ptr = p_value->prev_ptr; } else { - _data->last = value->prev_ptr; + _data->last = p_value->prev_ptr; } - value->next_ptr = where; - if (!where) { - value->prev_ptr = _data->last; - _data->last = value; + p_value->next_ptr = p_where; + if (!p_where) { + p_value->prev_ptr = _data->last; + _data->last = p_value; return; } - value->prev_ptr = where->prev_ptr; + p_value->prev_ptr = p_where->prev_ptr; - if (where->prev_ptr) { - where->prev_ptr->next_ptr = value; + if (p_where->prev_ptr) { + p_where->prev_ptr->next_ptr = p_value; } else { - _data->first = value; + _data->first = p_value; } - where->prev_ptr = value; + p_where->prev_ptr = p_value; } - /** - * simple insertion sort - */ - void sort() { sort_custom>(); } - template - void sort_custom_inplace() { - if (size() < 2) { - return; - } - - Element *from = front(); - Element *current = from; - Element *to = from; - - while (current) { - Element *next = current->next_ptr; - - if (from != current) { - current->prev_ptr = nullptr; - current->next_ptr = from; - - Element *find = from; - C less; - while (find && less(find->value, current->value)) { - current->prev_ptr = find; - current->next_ptr = find->next_ptr; - find = find->next_ptr; - } - - if (current->prev_ptr) { - current->prev_ptr->next_ptr = current; - } else { - from = current; - } - - if (current->next_ptr) { - current->next_ptr->prev_ptr = current; - } else { - to = current; - } - } else { - current->prev_ptr = nullptr; - current->next_ptr = nullptr; - } - - current = next; - } - _data->first = from; - _data->last = to; - } - - template - struct AuxiliaryComparator { - C compare; - _FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const { - return compare(a->value, b->value); - } - }; - template void sort_custom() { - //this version uses auxiliary memory for speed. - //if you don't want to use auxiliary memory, use the in_place version - - int s = size(); - if (s < 2) { + if (size() < 2) { return; } - Element **aux_buffer = memnew_arr(Element *, s); - - int idx = 0; - for (Element *E = front(); E; E = E->next_ptr) { - aux_buffer[idx] = E; - idx++; - } - - SortArray> sort; - sort.sort(aux_buffer, s); - - _data->first = aux_buffer[0]; - aux_buffer[0]->prev_ptr = nullptr; - aux_buffer[0]->next_ptr = aux_buffer[1]; - - _data->last = aux_buffer[s - 1]; - aux_buffer[s - 1]->prev_ptr = aux_buffer[s - 2]; - aux_buffer[s - 1]->next_ptr = nullptr; - - for (int i = 1; i < s - 1; i++) { - aux_buffer[i]->prev_ptr = aux_buffer[i - 1]; - aux_buffer[i]->next_ptr = aux_buffer[i + 1]; - } - - memdelete_arr(aux_buffer); + SortList sorter; + sorter.sort(_data->first, _data->last); } const void *id() const { return (void *)_data; } - /** - * copy constructor for the list - */ - List(const List &p_list) { + explicit List(const List &p_list) { const Element *it = p_list.front(); while (it) { push_back(it->get()); diff --git a/include/godot_cpp/templates/sort_list.h b/include/godot_cpp/templates/sort_list.hpp similarity index 98% rename from include/godot_cpp/templates/sort_list.h rename to include/godot_cpp/templates/sort_list.hpp index e53f01360..edf9d0924 100644 --- a/include/godot_cpp/templates/sort_list.h +++ b/include/godot_cpp/templates/sort_list.hpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* sort_list.h */ +/* sort_list.hpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/include/godot_cpp/templates/span.hpp b/include/godot_cpp/templates/span.hpp index 03f0ca262..0e9232615 100644 --- a/include/godot_cpp/templates/span.hpp +++ b/include/godot_cpp/templates/span.hpp @@ -40,7 +40,9 @@ bool are_spans_equal(const LHS *p_lhs, const RHS *p_rhs, size_t p_size) { if constexpr (std::is_same_v && std::is_fundamental_v) { // Optimize trivial type comparison. // is_trivially_equality_comparable would help, but it doesn't exist. - return memcmp(p_lhs, p_rhs, p_size * sizeof(LHS)) == 0; + // memcmp requires pointer argument to be valid even on size = 0 (C11 ยง7.24.1(2)). + // Span allows ptr arguments to be invalid as long as size = 0, so only call when p_size > 0. + return p_size == 0 || memcmp(p_lhs, p_rhs, p_size * sizeof(LHS)) == 0; } else { // Normal case: Need to iterate the array manually. for (size_t j = 0; j < p_size; j++) { @@ -130,7 +132,7 @@ class Span { /// Find the index of the given value using binary search. /// Note: Assumes that elements in the span are sorted. Otherwise, use find() instead. template > - constexpr uint64_t bisect(const T &p_value, bool p_before, Comparator compare = Comparator()) const; + constexpr uint64_t bisect(const T &p_value, bool p_before, Comparator p_compare = Comparator()) const; /// The caller is responsible to ensure size() > 0. constexpr T max() const; @@ -195,13 +197,13 @@ constexpr uint64_t Span::count(const T &p_val) const { template template -constexpr uint64_t Span::bisect(const T &p_value, bool p_before, Comparator compare) const { +constexpr uint64_t Span::bisect(const T &p_value, bool p_before, Comparator p_compare) const { uint64_t lo = 0; uint64_t hi = size(); if (p_before) { while (lo < hi) { const uint64_t mid = (lo + hi) / 2; - if (compare(ptr()[mid], p_value)) { + if (p_compare(ptr()[mid], p_value)) { lo = mid + 1; } else { hi = mid; @@ -210,7 +212,7 @@ constexpr uint64_t Span::bisect(const T &p_value, bool p_before, Comparator c } else { while (lo < hi) { const uint64_t mid = (lo + hi) / 2; - if (compare(p_value, ptr()[mid])) { + if (p_compare(p_value, ptr()[mid])) { hi = mid; } else { lo = mid + 1; diff --git a/include/godot_cpp/templates/vset.hpp b/include/godot_cpp/templates/vset.hpp index 13d6e996b..cdbe3fad3 100644 --- a/include/godot_cpp/templates/vset.hpp +++ b/include/godot_cpp/templates/vset.hpp @@ -33,46 +33,29 @@ #include namespace godot { - +/** + * Ordered set container with copy-on-write semantics. + * + * Core container guidance: + * https://docs.godotengine.org/en/latest/engine_details/architecture/core_types.html#containers + */ template -class VSet { +class _WARN_UNUSED_ VSet { Vector _data; +protected: _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { r_exact = false; if (_data.is_empty()) { return 0; } - int low = 0; - int high = _data.size() - 1; - const T *a = &_data[0]; - int middle = 0; - -#ifdef DEBUG_ENABLED - if (low > high) { - ERR_PRINT("low > high, this may be a bug"); - } -#endif - - while (low <= high) { - middle = (low + high) / 2; - - if (p_val < a[middle]) { - high = middle - 1; //search low end of array - } else if (a[middle] < p_val) { - low = middle + 1; //search high end of array - } else { - r_exact = true; - return middle; - } - } + int64_t pos = _data.span().bisect(p_val, true); - //return the position where this would be inserted - if (a[middle] < p_val) { - middle++; + if (pos < _data.size() && !(p_val < _data[pos]) && !(_data[pos] < p_val)) { + r_exact = true; } - return middle; + return pos; } _FORCE_INLINE_ int _find_exact(const T &p_val) const { @@ -80,23 +63,11 @@ class VSet { return -1; } - int low = 0; - int high = _data.size() - 1; - int middle; - const T *a = &_data[0]; - - while (low <= high) { - middle = (low + high) / 2; - - if (p_val < a[middle]) { - high = middle - 1; //search low end of array - } else if (a[middle] < p_val) { - low = middle + 1; //search high end of array - } else { - return middle; - } - } + int64_t pos = _data.span().bisect(p_val, true); + if (pos < _data.size() && !(p_val < _data[pos]) && !(_data[pos] < p_val)) { + return pos; + } return -1; } diff --git a/src/templates/hashfuncs.cpp b/src/templates/hashfuncs.cpp new file mode 100644 index 000000000..30182f364 --- /dev/null +++ b/src/templates/hashfuncs.cpp @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* hashfuncs.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#include + +namespace godot { + +uint32_t hash_murmur3_one_float(float p_in, uint32_t p_seed) { + union { + float f; + uint32_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.f = 0.0; + } else if (Math::is_nan(p_in)) { + u.f = Math::NaN; + } else { + u.f = p_in; + } + + return hash_murmur3_one_32(u.i, p_seed); +} + +uint32_t hash_murmur3_one_double(double p_in, uint32_t p_seed) { + union { + double d; + uint64_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = Math::NaN; + } else { + u.d = p_in; + } + + return hash_murmur3_one_64(u.i, p_seed); +} + +uint32_t hash_murmur3_buffer(const void *p_key, int p_length, const uint32_t p_seed) { + // Although not required, this is a random prime number. + const uint8_t *data = (const uint8_t *)p_key; + const int nblocks = p_length / 4; + + uint32_t h1 = p_seed; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); + + for (int i = -nblocks; i; i++) { + uint32_t k1 = blocks[i]; + + k1 *= c1; + k1 = hash_rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = hash_rotl32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); + + uint32_t k1 = 0; + + switch (p_length & 3) { + case 3: + k1 ^= tail[2] << 16; + [[fallthrough]]; + case 2: + k1 ^= tail[1] << 8; + [[fallthrough]]; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = hash_rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + }; + + // Finalize with additional bit mixing. + h1 ^= p_length; + return hash_fmix32(h1); +} + +uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev) { + union { + double d; + uint64_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = Math::NaN; + } else { + u.d = p_in; + } + + return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i); +} + +uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev) { + union { + double d; + uint64_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = Math::NaN; + } else { + u.d = p_in; + } + + return ((p_prev << 5) + p_prev) + u.i; +} +} //namespace godot diff --git a/tools/godotcpp.py b/tools/godotcpp.py index 917ae2814..0adb7073f 100644 --- a/tools/godotcpp.py +++ b/tools/godotcpp.py @@ -585,6 +585,7 @@ def _godot_cpp(env): *env.Glob("src/*.cpp"), *env.Glob("src/classes/*.cpp"), *env.Glob("src/core/*.cpp"), + *env.Glob("src/templates/*.cpp"), *env.Glob("src/variant/*.cpp"), *tuple(f for f in bindings if str(f).endswith(".cpp")), ]