Merge pull request #761 from bruvzg/sync_containers
commit
8dbaf5a7ff
File diff suppressed because it is too large
Load Diff
|
@ -114,6 +114,7 @@ typedef enum {
|
|||
GDNATIVE_VARIANT_OP_NEGATE,
|
||||
GDNATIVE_VARIANT_OP_POSITIVE,
|
||||
GDNATIVE_VARIANT_OP_MODULE,
|
||||
GDNATIVE_VARIANT_OP_POWER,
|
||||
/* bitwise */
|
||||
GDNATIVE_VARIANT_OP_SHIFT_LEFT,
|
||||
GDNATIVE_VARIANT_OP_SHIFT_RIGHT,
|
||||
|
@ -412,6 +413,8 @@ typedef struct {
|
|||
GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
|
||||
GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
|
||||
void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
|
||||
GDNativeInt (*variant_hash)(const GDNativeVariantPtr p_self);
|
||||
GDNativeInt (*variant_recursive_hash)(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count);
|
||||
GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other);
|
||||
GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self);
|
||||
void (*variant_sub)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst);
|
||||
|
|
|
@ -98,6 +98,14 @@ public:
|
|||
_ALWAYS_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DefaultTypedAllocator {
|
||||
public:
|
||||
template <class... Args>
|
||||
_ALWAYS_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
|
||||
_ALWAYS_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = 0) {
|
||||
if (!__has_trivial_destructor(T)) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,477 @@
|
|||
/*************************************************************************/
|
||||
/* hash_set.hpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef HASH_SET_HPP
|
||||
#define HASH_SET_HPP
|
||||
|
||||
#include <godot_cpp/core/error_macros.hpp>
|
||||
#include <godot_cpp/core/memory.hpp>
|
||||
#include <godot_cpp/templates/hash_map.hpp>
|
||||
#include <godot_cpp/templates/hashfuncs.hpp>
|
||||
#include <godot_cpp/templates/pair.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
/**
|
||||
* Implementation of Set using a bidi indexed hash map.
|
||||
* Use RBSet instead of this only if the following conditions are met:
|
||||
*
|
||||
* - 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<)
|
||||
*
|
||||
*/
|
||||
|
||||
template <class TKey,
|
||||
class Hasher = HashMapHasherDefault,
|
||||
class Comparator = HashMapComparatorDefault<TKey>>
|
||||
class 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;
|
||||
|
||||
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);
|
||||
|
||||
if (unlikely(hash == EMPTY_HASH)) {
|
||||
hash = EMPTY_HASH + 1;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_capacity) const {
|
||||
uint32_t original_pos = p_hash % p_capacity;
|
||||
return (p_pos - original_pos + p_capacity) % p_capacity;
|
||||
}
|
||||
|
||||
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
|
||||
if (keys == nullptr) {
|
||||
return false; // Failed lookups, no elements
|
||||
}
|
||||
|
||||
uint32_t capacity = hash_table_size_primes[capacity_index];
|
||||
uint32_t hash = _hash(p_key);
|
||||
uint32_t pos = hash % capacity;
|
||||
uint32_t distance = 0;
|
||||
|
||||
while (true) {
|
||||
if (hashes[pos] == EMPTY_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distance > _get_probe_length(pos, hashes[pos], capacity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hashes[pos] == hash && Comparator::compare(keys[hash_to_key[pos]], p_key)) {
|
||||
r_pos = hash_to_key[pos];
|
||||
return true;
|
||||
}
|
||||
|
||||
pos = (pos + 1) % capacity;
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) {
|
||||
uint32_t capacity = hash_table_size_primes[capacity_index];
|
||||
uint32_t hash = p_hash;
|
||||
uint32_t index = p_index;
|
||||
uint32_t distance = 0;
|
||||
uint32_t pos = hash % capacity;
|
||||
|
||||
while (true) {
|
||||
if (hashes[pos] == EMPTY_HASH) {
|
||||
hashes[pos] = hash;
|
||||
key_to_hash[index] = pos;
|
||||
hash_to_key[pos] = index;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (existing_probe_len < distance) {
|
||||
key_to_hash[index] = pos;
|
||||
SWAP(hash, hashes[pos]);
|
||||
SWAP(index, hash_to_key[pos]);
|
||||
distance = existing_probe_len;
|
||||
}
|
||||
|
||||
pos = (pos + 1) % capacity;
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
|
||||
void _resize_and_rehash(uint32_t p_new_capacity_index) {
|
||||
// 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];
|
||||
|
||||
uint32_t *old_hashes = hashes;
|
||||
uint32_t *old_key_to_hash = key_to_hash;
|
||||
|
||||
hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
keys = reinterpret_cast<TKey *>(Memory::realloc_static(keys, sizeof(TKey) * capacity));
|
||||
key_to_hash = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
hash_to_key = reinterpret_cast<uint32_t *>(Memory::realloc_static(hash_to_key, sizeof(uint32_t) * capacity));
|
||||
|
||||
for (uint32_t i = 0; i < capacity; i++) {
|
||||
hashes[i] = EMPTY_HASH;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
uint32_t h = old_hashes[old_key_to_hash[i]];
|
||||
_insert_with_hash(h, i);
|
||||
}
|
||||
|
||||
Memory::free_static(old_hashes);
|
||||
Memory::free_static(old_key_to_hash);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int32_t _insert(const TKey &p_key) {
|
||||
uint32_t capacity = hash_table_size_primes[capacity_index];
|
||||
if (unlikely(keys == nullptr)) {
|
||||
// Allocate on demand to save memory.
|
||||
|
||||
hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
keys = reinterpret_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity));
|
||||
key_to_hash = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
hash_to_key = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
|
||||
for (uint32_t i = 0; i < capacity; i++) {
|
||||
hashes[i] = EMPTY_HASH;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos);
|
||||
|
||||
if (exists) {
|
||||
return pos;
|
||||
} 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void _init_from(const HashSet &p_other) {
|
||||
capacity_index = p_other.capacity_index;
|
||||
num_elements = p_other.num_elements;
|
||||
|
||||
if (p_other.num_elements == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t capacity = hash_table_size_primes[capacity_index];
|
||||
|
||||
hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
keys = reinterpret_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity));
|
||||
key_to_hash = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
|
||||
hash_to_key = reinterpret_cast<uint32_t *>(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 < capacity; i++) {
|
||||
hashes[i] = p_other.hashes[i];
|
||||
hash_to_key[i] = p_other.hash_to_key[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; }
|
||||
|
||||
/* Standard Godot Container API */
|
||||
|
||||
bool is_empty() const {
|
||||
return num_elements == 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (keys == nullptr) {
|
||||
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();
|
||||
}
|
||||
|
||||
num_elements = 0;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool has(const TKey &p_key) const {
|
||||
uint32_t _pos = 0;
|
||||
return _lookup_pos(p_key, _pos);
|
||||
}
|
||||
|
||||
bool erase(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos);
|
||||
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t key_pos = pos;
|
||||
pos = key_to_hash[pos]; // make hash pos
|
||||
|
||||
uint32_t capacity = hash_table_size_primes[capacity_index];
|
||||
uint32_t next_pos = (pos + 1) % capacity;
|
||||
while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity) != 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]);
|
||||
|
||||
pos = next_pos;
|
||||
next_pos = (pos + 1) % 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
if (new_index == capacity_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (keys == nullptr) {
|
||||
capacity_index = new_index;
|
||||
return; // Unallocated yet.
|
||||
}
|
||||
_resize_and_rehash(new_index);
|
||||
}
|
||||
|
||||
/** Iterator API **/
|
||||
|
||||
struct Iterator {
|
||||
_FORCE_INLINE_ const TKey &operator*() const {
|
||||
return keys[index];
|
||||
}
|
||||
_FORCE_INLINE_ const TKey *operator->() const {
|
||||
return &keys[index];
|
||||
}
|
||||
_FORCE_INLINE_ Iterator &operator++() {
|
||||
index++;
|
||||
if (index >= (int32_t)num_keys) {
|
||||
index = -1;
|
||||
keys = nullptr;
|
||||
num_keys = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator &operator--() {
|
||||
index--;
|
||||
if (index < 0) {
|
||||
index = -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_ explicit operator bool() const {
|
||||
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() {}
|
||||
_FORCE_INLINE_ Iterator(const Iterator &p_it) {
|
||||
keys = p_it.keys;
|
||||
num_keys = p_it.num_keys;
|
||||
index = p_it.index;
|
||||
}
|
||||
_FORCE_INLINE_ void operator=(const Iterator &p_it) {
|
||||
keys = p_it.keys;
|
||||
num_keys = p_it.num_keys;
|
||||
index = p_it.index;
|
||||
}
|
||||
|
||||
private:
|
||||
const TKey *keys = nullptr;
|
||||
uint32_t num_keys = 0;
|
||||
int32_t index = -1;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Iterator begin() const {
|
||||
return num_elements ? Iterator(keys, num_elements, 0) : Iterator();
|
||||
}
|
||||
_FORCE_INLINE_ Iterator end() const {
|
||||
return Iterator();
|
||||
}
|
||||
_FORCE_INLINE_ Iterator last() const {
|
||||
if (num_elements == 0) {
|
||||
return Iterator();
|
||||
}
|
||||
return Iterator(keys, num_elements, num_elements - 1);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Iterator find(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos);
|
||||
if (!exists) {
|
||||
return end();
|
||||
}
|
||||
return Iterator(keys, num_elements, pos);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void remove(const Iterator &p_iter) {
|
||||
if (p_iter) {
|
||||
erase(*p_iter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert */
|
||||
|
||||
Iterator insert(const TKey &p_key) {
|
||||
uint32_t pos = _insert(p_key);
|
||||
return Iterator(keys, num_elements, pos);
|
||||
}
|
||||
|
||||
/* Constructors */
|
||||
|
||||
HashSet(const HashSet &p_other) {
|
||||
_init_from(p_other);
|
||||
}
|
||||
|
||||
void operator=(const HashSet &p_other) {
|
||||
if (this == &p_other) {
|
||||
return; // Ignore self assignment.
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_init_from(p_other);
|
||||
}
|
||||
|
||||
HashSet(uint32_t p_initial_capacity) {
|
||||
// Capacity can't be 0.
|
||||
capacity_index = 0;
|
||||
reserve(p_initial_capacity);
|
||||
}
|
||||
HashSet() {
|
||||
capacity_index = MIN_CAPACITY_INDEX;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
~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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace godot
|
||||
|
||||
#endif // HASH_SET_HPP
|
|
@ -32,7 +32,19 @@
|
|||
#define HASHFUNCS_HPP
|
||||
|
||||
#include <godot_cpp/core/math.hpp>
|
||||
#include <godot_cpp/core/object.hpp>
|
||||
#include <godot_cpp/variant/aabb.hpp>
|
||||
#include <godot_cpp/variant/node_path.hpp>
|
||||
#include <godot_cpp/variant/rect2.hpp>
|
||||
#include <godot_cpp/variant/rect2i.hpp>
|
||||
#include <godot_cpp/variant/rid.hpp>
|
||||
#include <godot_cpp/variant/string.hpp>
|
||||
#include <godot_cpp/variant/string_name.hpp>
|
||||
#include <godot_cpp/variant/variant.hpp>
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
#include <godot_cpp/variant/vector2i.hpp>
|
||||
#include <godot_cpp/variant/vector3.hpp>
|
||||
#include <godot_cpp/variant/vector3i.hpp>
|
||||
|
||||
/**
|
||||
* Hashing functions
|
||||
|
@ -152,9 +164,14 @@ static inline uint64_t make_uint64_t(T p_in) {
|
|||
return _u._u64;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class Ref;
|
||||
|
||||
struct HashMapHasherDefault {
|
||||
static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); }
|
||||
|
@ -169,6 +186,60 @@ struct HashMapHasherDefault {
|
|||
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return (uint32_t)p_uchar; }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return (uint32_t)p_uchar; }
|
||||
static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
|
||||
|
||||
template <class T>
|
||||
static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); }
|
||||
|
||||
template <class T>
|
||||
static _FORCE_INLINE_ uint32_t hash(const Ref<T> &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); }
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) {
|
||||
uint32_t h = hash_djb2_one_32(p_vec.x);
|
||||
return hash_djb2_one_32(p_vec.y, h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
|
||||
uint32_t h = hash_djb2_one_32(p_vec.x);
|
||||
h = hash_djb2_one_32(p_vec.y, h);
|
||||
return hash_djb2_one_32(p_vec.z, h);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
|
||||
uint32_t h = hash_djb2_one_float(p_vec.x);
|
||||
return hash_djb2_one_float(p_vec.y, h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) {
|
||||
uint32_t h = hash_djb2_one_float(p_vec.x);
|
||||
h = hash_djb2_one_float(p_vec.y, h);
|
||||
return hash_djb2_one_float(p_vec.z, h);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
|
||||
uint32_t h = hash_djb2_one_32(p_rect.position.x);
|
||||
h = hash_djb2_one_32(p_rect.position.y, h);
|
||||
h = hash_djb2_one_32(p_rect.size.x, h);
|
||||
return hash_djb2_one_32(p_rect.size.y, h);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {
|
||||
uint32_t h = hash_djb2_one_float(p_rect.position.x);
|
||||
h = hash_djb2_one_float(p_rect.position.y, h);
|
||||
h = hash_djb2_one_float(p_rect.size.x, h);
|
||||
return hash_djb2_one_float(p_rect.size.y, h);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) {
|
||||
uint32_t h = hash_djb2_one_float(p_aabb.position.x);
|
||||
h = hash_djb2_one_float(p_aabb.position.y, h);
|
||||
h = hash_djb2_one_float(p_aabb.position.z, h);
|
||||
h = hash_djb2_one_float(p_aabb.size.x, h);
|
||||
h = hash_djb2_one_float(p_aabb.size.y, h);
|
||||
return hash_djb2_one_float(p_aabb.size.z, h);
|
||||
}
|
||||
|
||||
// static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -176,16 +247,70 @@ struct HashMapComparatorDefault {
|
|||
static bool compare(const T &p_lhs, const T &p_rhs) {
|
||||
return p_lhs == p_rhs;
|
||||
}
|
||||
};
|
||||
|
||||
bool compare(const float &p_lhs, const float &p_rhs) {
|
||||
return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
|
||||
}
|
||||
|
||||
bool compare(const double &p_lhs, const double &p_rhs) {
|
||||
template <>
|
||||
struct HashMapComparatorDefault<float> {
|
||||
static bool compare(const float &p_lhs, const float &p_rhs) {
|
||||
return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<double> {
|
||||
static bool compare(const double &p_lhs, const double &p_rhs) {
|
||||
return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Vector2> {
|
||||
static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
|
||||
return ((p_lhs.x == p_rhs.x) || (std::isnan(p_lhs.x) && std::isnan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (std::isnan(p_lhs.y) && std::isnan(p_rhs.y)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Vector3> {
|
||||
static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) {
|
||||
return ((p_lhs.x == p_rhs.x) || (std::isnan(p_lhs.x) && std::isnan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (std::isnan(p_lhs.y) && std::isnan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (std::isnan(p_lhs.z) && std::isnan(p_rhs.z)));
|
||||
}
|
||||
};
|
||||
|
||||
constexpr uint32_t HASH_TABLE_SIZE_MAX = 29;
|
||||
|
||||
const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
|
||||
5,
|
||||
13,
|
||||
23,
|
||||
47,
|
||||
97,
|
||||
193,
|
||||
389,
|
||||
769,
|
||||
1543,
|
||||
3079,
|
||||
6151,
|
||||
12289,
|
||||
24593,
|
||||
49157,
|
||||
98317,
|
||||
196613,
|
||||
393241,
|
||||
786433,
|
||||
1572869,
|
||||
3145739,
|
||||
6291469,
|
||||
12582917,
|
||||
25165843,
|
||||
50331653,
|
||||
100663319,
|
||||
201326611,
|
||||
402653189,
|
||||
805306457,
|
||||
1610612741,
|
||||
};
|
||||
|
||||
} // namespace godot
|
||||
|
||||
#endif // ! HASHFUNCS_HPP
|
||||
#endif // HASHFUNCS_HPP
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*************************************************************************/
|
||||
/* map.hpp */
|
||||
/* rb_map.hpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
|
@ -28,8 +28,8 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef MAP_HPP
|
||||
#define MAP_HPP
|
||||
#ifndef RB_MAP_HPP
|
||||
#define RB_MAP_HPP
|
||||
|
||||
#include <godot_cpp/core/error_macros.hpp>
|
||||
#include <godot_cpp/core/memory.hpp>
|
||||
|
@ -41,7 +41,7 @@ namespace godot {
|
|||
// https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
|
||||
|
||||
template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
|
||||
class Map {
|
||||
class RBMap {
|
||||
enum Color {
|
||||
RED,
|
||||
BLACK
|
||||
|
@ -51,7 +51,7 @@ class Map {
|
|||
public:
|
||||
class Element {
|
||||
private:
|
||||
friend class Map<K, V, C, A>;
|
||||
friend class RBMap<K, V, C, A>;
|
||||
int color = RED;
|
||||
Element *right = nullptr;
|
||||
Element *left = nullptr;
|
||||
|
@ -113,7 +113,9 @@ public:
|
|||
|
||||
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
|
||||
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
|
||||
|
||||
explicit operator bool() const {
|
||||
return E != nullptr;
|
||||
}
|
||||
Iterator(Element *p_E) { E = p_E; }
|
||||
Iterator() {}
|
||||
Iterator(const Iterator &p_it) { E = p_it.E; }
|
||||
|
@ -138,7 +140,9 @@ public:
|
|||
|
||||
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
|
||||
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
|
||||
|
||||
explicit operator bool() const {
|
||||
return E != nullptr;
|
||||
}
|
||||
ConstIterator(const Element *p_E) { E = p_E; }
|
||||
ConstIterator() {}
|
||||
ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
|
||||
|
@ -180,7 +184,7 @@ public:
|
|||
private:
|
||||
struct _Data {
|
||||
Element *_root = nullptr;
|
||||
Element *_nil;
|
||||
Element *_nil = nullptr;
|
||||
int size_cache = 0;
|
||||
|
||||
_FORCE_INLINE_ _Data() {
|
||||
|
@ -346,7 +350,7 @@ private:
|
|||
void _insert_rb_fix(Element *p_new_node) {
|
||||
Element *node = p_new_node;
|
||||
Element *nparent = node->parent;
|
||||
Element *ngrand_parent;
|
||||
Element *ngrand_parent = nullptr;
|
||||
|
||||
while (nparent->color == RED) {
|
||||
ngrand_parent = nparent->parent;
|
||||
|
@ -502,7 +506,7 @@ private:
|
|||
Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
|
||||
Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
|
||||
|
||||
Element *sibling;
|
||||
Element *sibling = nullptr;
|
||||
if (rp == rp->parent->left) {
|
||||
rp->parent->left = node;
|
||||
sibling = rp->parent->right;
|
||||
|
@ -574,7 +578,7 @@ private:
|
|||
memdelete_allocator<Element, A>(p_element);
|
||||
}
|
||||
|
||||
void _copy_from(const Map &p_map) {
|
||||
void _copy_from(const RBMap &p_map) {
|
||||
clear();
|
||||
// not the fastest way, but safeset to write.
|
||||
for (Element *I = p_map.front(); I; I = I->next()) {
|
||||
|
@ -712,8 +716,12 @@ public:
|
|||
return e;
|
||||
}
|
||||
|
||||
inline bool is_empty() const { return _data.size_cache == 0; }
|
||||
inline int size() const { return _data.size_cache; }
|
||||
inline bool is_empty() const {
|
||||
return _data.size_cache == 0;
|
||||
}
|
||||
inline int size() const {
|
||||
return _data.size_cache;
|
||||
}
|
||||
|
||||
int calculate_depth() const {
|
||||
// used for debug mostly
|
||||
|
@ -737,21 +745,21 @@ public:
|
|||
_data._free_root();
|
||||
}
|
||||
|
||||
void operator=(const Map &p_map) {
|
||||
void operator=(const RBMap &p_map) {
|
||||
_copy_from(p_map);
|
||||
}
|
||||
|
||||
Map(const Map &p_map) {
|
||||
RBMap(const RBMap &p_map) {
|
||||
_copy_from(p_map);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Map() {}
|
||||
_FORCE_INLINE_ RBMap() {}
|
||||
|
||||
~Map() {
|
||||
~RBMap() {
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace godot
|
||||
|
||||
#endif // ! MAP_HPP
|
||||
#endif // MAP_HPP
|
|
@ -1,5 +1,5 @@
|
|||
/*************************************************************************/
|
||||
/* set.hpp */
|
||||
/* rb_set.hpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
|
@ -28,8 +28,8 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef SET_HPP
|
||||
#define SET_HPP
|
||||
#ifndef RB_SET_HPP
|
||||
#define RB_SET_HPP
|
||||
|
||||
#include <godot_cpp/core/memory.hpp>
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
|||
namespace godot {
|
||||
|
||||
template <class T, class C = Comparator<T>, class A = DefaultAllocator>
|
||||
class Set {
|
||||
class RBSet {
|
||||
enum Color {
|
||||
RED,
|
||||
BLACK
|
||||
|
@ -49,7 +49,7 @@ class Set {
|
|||
public:
|
||||
class Element {
|
||||
private:
|
||||
friend class Set<T, C, A>;
|
||||
friend class RBSet<T, C, A>;
|
||||
int color = RED;
|
||||
Element *right = nullptr;
|
||||
Element *left = nullptr;
|
||||
|
@ -100,6 +100,7 @@ public:
|
|||
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
|
||||
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
|
||||
|
||||
explicit operator bool() const { return E != nullptr; }
|
||||
Iterator(Element *p_E) { E = p_E; }
|
||||
Iterator() {}
|
||||
Iterator(const Iterator &p_it) { E = p_it.E; }
|
||||
|
@ -129,6 +130,8 @@ public:
|
|||
_FORCE_INLINE_ ConstIterator() {}
|
||||
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
|
||||
|
||||
explicit operator bool() const { return E != nullptr; }
|
||||
|
||||
private:
|
||||
const Element *E = nullptr;
|
||||
};
|
||||
|
@ -329,7 +332,7 @@ private:
|
|||
void _insert_rb_fix(Element *p_new_node) {
|
||||
Element *node = p_new_node;
|
||||
Element *nparent = node->parent;
|
||||
Element *ngrand_parent;
|
||||
Element *ngrand_parent = nullptr;
|
||||
|
||||
while (nparent->color == RED) {
|
||||
ngrand_parent = nparent->parent;
|
||||
|
@ -483,7 +486,7 @@ private:
|
|||
Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
|
||||
Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
|
||||
|
||||
Element *sibling;
|
||||
Element *sibling = nullptr;
|
||||
if (rp == rp->parent->left) {
|
||||
rp->parent->left = node;
|
||||
sibling = rp->parent->right;
|
||||
|
@ -555,7 +558,7 @@ private:
|
|||
memdelete_allocator<Element, A>(p_element);
|
||||
}
|
||||
|
||||
void _copy_from(const Set &p_set) {
|
||||
void _copy_from(const RBSet &p_set) {
|
||||
clear();
|
||||
// not the fastest way, but safeset to write.
|
||||
for (Element *I = p_set.front(); I; I = I->next()) {
|
||||
|
@ -662,8 +665,12 @@ public:
|
|||
return e;
|
||||
}
|
||||
|
||||
inline bool is_empty() const { return _data.size_cache == 0; }
|
||||
inline int size() const { return _data.size_cache; }
|
||||
inline bool is_empty() const {
|
||||
return _data.size_cache == 0;
|
||||
}
|
||||
inline int size() const {
|
||||
return _data.size_cache;
|
||||
}
|
||||
|
||||
int calculate_depth() const {
|
||||
// used for debug mostly
|
||||
|
@ -687,21 +694,21 @@ public:
|
|||
_data._free_root();
|
||||
}
|
||||
|
||||
void operator=(const Set &p_set) {
|
||||
void operator=(const RBSet &p_set) {
|
||||
_copy_from(p_set);
|
||||
}
|
||||
|
||||
Set(const Set &p_set) {
|
||||
RBSet(const RBSet &p_set) {
|
||||
_copy_from(p_set);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Set() {}
|
||||
_FORCE_INLINE_ RBSet() {}
|
||||
|
||||
~Set() {
|
||||
~RBSet() {
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace godot
|
||||
|
||||
#endif // ! SET_HPP
|
||||
#endif // SET_HPP
|
|
@ -285,6 +285,8 @@ public:
|
|||
bool has_key(const Variant &key, bool *r_valid = nullptr) const;
|
||||
static bool has_member(Variant::Type type, const StringName &member);
|
||||
|
||||
uint32_t hash() const;
|
||||
uint32_t recursive_hash(int recursion_count) const;
|
||||
bool hash_compare(const Variant &variant) const;
|
||||
bool booleanize() const;
|
||||
String stringify() const;
|
||||
|
@ -299,6 +301,14 @@ public:
|
|||
void clear();
|
||||
};
|
||||
|
||||
struct VariantHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); }
|
||||
};
|
||||
|
||||
struct VariantComparator {
|
||||
static _FORCE_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) { return p_lhs.hash_compare(p_rhs); }
|
||||
};
|
||||
|
||||
} // namespace godot
|
||||
|
||||
#endif // ! GODOT_CPP_VARIANT_HPP
|
||||
|
|
|
@ -629,6 +629,16 @@ bool Variant::has_member(Variant::Type type, const StringName &member) {
|
|||
return PtrToArg<bool>::convert(&has);
|
||||
}
|
||||
|
||||
uint32_t Variant::hash() const {
|
||||
GDNativeInt hash = internal::gdn_interface->variant_hash(_native_ptr());
|
||||
return PtrToArg<uint32_t>::convert(&hash);
|
||||
}
|
||||
|
||||
uint32_t Variant::recursive_hash(int recursion_count) const {
|
||||
GDNativeInt hash = internal::gdn_interface->variant_recursive_hash(_native_ptr(), recursion_count);
|
||||
return PtrToArg<uint32_t>::convert(&hash);
|
||||
}
|
||||
|
||||
bool Variant::hash_compare(const Variant &variant) const {
|
||||
GDNativeBool compare = internal::gdn_interface->variant_hash_compare(_native_ptr(), variant._native_ptr());
|
||||
return PtrToArg<bool>::convert(&compare);
|
||||
|
|
Loading…
Reference in New Issue