Merge pull request #761 from bruvzg/sync_containers

pull/763/head
Rémi Verschelde 2022-06-06 13:30:27 +02:00 committed by GitHub
commit 8dbaf5a7ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 9401 additions and 8094 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);